├── book ├── src │ ├── welcome.md │ ├── migrating.md │ ├── SUMMARY.md │ ├── tricks.md │ ├── getting-started.md │ ├── initial.md │ └── contributing.md └── book.toml ├── triagebot.toml ├── rust-toolchain.toml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── rustc_public ├── src │ ├── mir.rs │ ├── rustc_internal │ │ ├── pretty.rs │ │ └── mod.rs │ ├── unstable │ │ ├── internal_cx │ │ │ ├── helpers.rs │ │ │ └── mod.rs │ │ ├── convert │ │ │ ├── mod.rs │ │ │ └── stable │ │ │ │ ├── mod.rs │ │ │ │ └── abi.rs │ │ └── mod.rs │ ├── target.rs │ ├── error.rs │ ├── alloc.rs │ ├── mir │ │ ├── alloc.rs │ │ └── mono.rs │ ├── crate_def.rs │ ├── visitor.rs │ ├── lib.rs │ └── abi.rs ├── tests │ ├── fixme │ │ ├── ice_too_generic.rs │ │ └── ice_too_generic.stderr │ ├── print │ │ ├── async-closure.rs │ │ ├── basic_function.rs │ │ ├── operands.rs │ │ ├── basic_function.stdout │ │ ├── async-closure.stdout │ │ └── operands.stdout │ ├── sanity-checks │ │ ├── simple │ │ │ └── simple_lib.rs │ │ └── associated-items │ │ │ └── methods.rs │ └── compiletest.rs ├── build.rs ├── Cargo.toml └── README.md ├── demo ├── rust-toolchain.toml ├── run_demo.sh ├── Cargo.toml ├── build.rs ├── example │ └── methods.rs └── src │ └── main.rs ├── Cargo.toml ├── devtool ├── Cargo.toml └── src │ ├── utils.rs │ └── main.rs ├── .github └── workflows │ ├── demo.yml │ ├── format.yml │ ├── nightly.yml │ └── deploy_mdbook.yml ├── test-drive ├── Cargo.toml ├── build.rs └── src │ ├── main.rs │ └── sanity_checks.rs ├── rustfmt.toml ├── x ├── CHARTER.md ├── scripts └── pre-push.sh ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /book/src/welcome.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /triagebot.toml: -------------------------------------------------------------------------------- 1 | # This will allow users to self assign, and/or drop assignment 2 | [assign] 3 | 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /book/build 2 | **/target 3 | 4 | # direnv 5 | .envrc 6 | /.direnv 7 | 8 | .idea 9 | *.swp 10 | *.swo 11 | .vscode 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # The Rust Code of Conduct 2 | 3 | The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). 4 | -------------------------------------------------------------------------------- /book/src/migrating.md: -------------------------------------------------------------------------------- 1 | # Migrating to StableMIR 2 | 3 | For now, we recommend looking at 4 | the [Kani migration documentation](https://model-checking.github.io/kani/stable-mir.html). -------------------------------------------------------------------------------- /rustc_public/src/mir.rs: -------------------------------------------------------------------------------- 1 | pub mod alloc; 2 | mod body; 3 | pub mod mono; 4 | pub mod pretty; 5 | pub mod visit; 6 | 7 | pub use body::*; 8 | pub use visit::{MirVisitor, MutMirVisitor}; 9 | -------------------------------------------------------------------------------- /demo/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # Most users will want to pin down a specific version, e.g.: 3 | # channel = "nightly-2024-06-17" 4 | channel = "nightly" 5 | components = ["llvm-tools", "rustc-dev", "rust-src"] 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "devtool", 5 | "rustc_public", 6 | "test-drive", 7 | ] 8 | 9 | exclude = [ 10 | "build", 11 | "demo", 12 | "target", 13 | ] 14 | -------------------------------------------------------------------------------- /demo/run_demo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Builds and run the demo driver against an example. 3 | 4 | REPO_DIR=$(git rev-parse --show-toplevel) 5 | DEMO_DIR="${REPO_DIR}/demo" 6 | 7 | cd "${DEMO_DIR}" 8 | cargo run -- example/methods.rs --crate-name exp --edition 2021 -C panic=abort 9 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Project StableMIR 2 | 3 | - [Welcome](./welcome.md) 4 | - [Getting Started](./getting-started.md) 5 | - [Initial Integration](./initial.md) 6 | - [Migrating to StableMIR](./migrating.md) 7 | - [Tool Development: Tips and Tricks](./tricks.md) 8 | - [Contributing](./contributing.md) -------------------------------------------------------------------------------- /rustc_public/tests/fixme/ice_too_generic.rs: -------------------------------------------------------------------------------- 1 | //@compile-flags: -Copt-level=1 2 | 3 | #![allow(dead_code, unused_variables)] 4 | use std::fmt::Debug; 5 | 6 | pub trait Meow { 7 | fn foo(&self, a: Option<&A>) -> A; 8 | 9 | fn fzz(&self) -> A { 10 | self.foo(None) 11 | } 12 | } 13 | 14 | fn main() {} -------------------------------------------------------------------------------- /rustc_public/tests/fixme/ice_too_generic.stderr: -------------------------------------------------------------------------------- 1 | 2 | thread 'rustc' ($TID) panicked at rustc_public/src/alloc.rs:LL:CC: 3 | Failed to convert: Scalar($HEX) to std::option::Option<&'{erased} A/#1> 4 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 5 | Test sanity_checks::test_all_fns: Failed: 6 | - Panic! 7 | -------------------------------------------------------------------------------- /devtool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "devtool" 3 | version = "0.0.0" 4 | authors = ["rustc_public team"] 5 | license = "MIT OR Apache-2.0" 6 | repository = "https://github.com/rust-lang/project-stable-mir" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | anyhow = "1.0.99" 11 | clap = { version = "4.1.3", features = ["derive"] } 12 | xshell = "0.2.6" 13 | -------------------------------------------------------------------------------- /rustc_public/tests/print/async-closure.rs: -------------------------------------------------------------------------------- 1 | //@ compile-flags: -Z unpretty=stable-mir --crate-type lib -C panic=abort -Zmir-opt-level=0 -Ztrim-diagnostic-paths=true 2 | //@check-pass 3 | //@ edition: 2024 4 | 5 | #![allow(dead_code, unused_variables)] 6 | 7 | pub fn foo() { 8 | let y = 0; 9 | let x = async || { 10 | let y = y; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rpub-demo" 3 | description = "A little demo tool on how to use rustc_public to analyze crates" 4 | version = "0.0.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | 9 | [package.metadata.rust-analyzer] 10 | # This crate uses #[feature(rustc_private)]. 11 | # See https://github.com/rust-analyzer/rust-analyzer/pull/7891 12 | rustc_private = true 13 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rustc Librarification Project (formerly Stable MIR)" 3 | language = "en" 4 | 5 | [output.html] 6 | curly-quotes = true 7 | git-repository-url = "https://github.com/rust-lang/project-stable-mir" 8 | site-url = "/project-stable-mir/" 9 | 10 | [output.html.playground] 11 | runnable = false 12 | 13 | [rust] 14 | edition = "2021" 15 | 16 | [build] 17 | build-dir = "build" -------------------------------------------------------------------------------- /.github/workflows/demo.yml: -------------------------------------------------------------------------------- 1 | # Run a job to ensure that we can run the demo successfully. 2 | name: Run demo 3 | on: 4 | pull_request: 5 | paths: 6 | - demo/** 7 | 8 | push: 9 | paths: 10 | - demo/** 11 | 12 | jobs: 13 | check_demo: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Run Demo 20 | run: ./demo/run_demo.sh 21 | -------------------------------------------------------------------------------- /test-drive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-drive" 3 | description = "A rustc wrapper that can be used to test rustc_public on a crate" 4 | version = "0.0.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } 9 | 10 | [package.metadata.rust-analyzer] 11 | # This crate uses #[feature(rustc_private)]. 12 | # See https://github.com/rust-analyzer/rust-analyzer/pull/7891 13 | rustc_private = true 14 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | # Run a job to ensure formatting is OK 2 | name: Format Check 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | format-check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Rust Toolchain 17 | uses: dtolnay/rust-toolchain@nightly 18 | 19 | - name: Run Rust Format 20 | run: ./x fmt --check 21 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Run rustfmt with this config (it should be picked up automatically). 2 | # Matches the rustfmt.toml of rustc. 3 | style_edition = "2024" 4 | use_small_heuristics = "Max" 5 | merge_derives = false 6 | group_imports = "StdExternalCrate" 7 | imports_granularity = "Module" 8 | use_field_init_shorthand = true 9 | 10 | # Files to ignore. 11 | ignore = [ 12 | # Do not format tests. 13 | "rustc_public/tests", 14 | 15 | # Do not format build.rs. 16 | "build.rs", 17 | ] 18 | -------------------------------------------------------------------------------- /test-drive/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | pub fn main() { 5 | // Add rustup to the rpath in order to properly link with the correct rustc version. 6 | let rustup_home = env::var("RUSTUP_HOME").unwrap(); 7 | let toolchain = env::var("RUSTUP_TOOLCHAIN").unwrap(); 8 | let rustc_lib: PathBuf = [&rustup_home, "toolchains", &toolchain, "lib"].iter().collect(); 9 | println!("cargo:rustc-link-arg-bin=test-drive=-Wl,-rpath,{}", rustc_lib.display()); 10 | } 11 | -------------------------------------------------------------------------------- /x: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | # Assume we are inside rustc_public repository 5 | ROOT_DIR=$(git rev-parse --show-toplevel) 6 | # Mix the outputs so that we can do `cargo clean` in one go. 7 | TARGET_DIR="$ROOT_DIR"/target 8 | 9 | REPO_TOOLCHAIN=$(rustup show active-toolchain | (read toolchain _; echo $toolchain)) 10 | TOOLCHAIN="${TOOLCHAIN:-${REPO_TOOLCHAIN}}" 11 | 12 | cargo +${TOOLCHAIN} build --manifest-path "${ROOT_DIR}"/devtool/Cargo.toml \ 13 | --target-dir "${TARGET_DIR}" 14 | 15 | "${TARGET_DIR}"/debug/devtool "$@" 16 | -------------------------------------------------------------------------------- /rustc_public/tests/print/basic_function.rs: -------------------------------------------------------------------------------- 1 | //@ compile-flags: -Z unpretty=stable-mir -Zmir-opt-level=0 -Ztrim-diagnostic-paths=true 2 | //@check-pass 3 | 4 | #![allow(dead_code, unused_variables)] 5 | 6 | fn foo(i: i32) -> i32 { 7 | i + 1 8 | } 9 | 10 | fn bar(vec: &mut Vec) -> Vec { 11 | let mut new_vec = vec.clone(); 12 | new_vec.push(1); 13 | new_vec 14 | } 15 | 16 | pub fn demux(input: u8) -> u8 { 17 | match input { 18 | 0 => 10, 19 | 1 => 6, 20 | 2 => 8, 21 | _ => 0, 22 | } 23 | } 24 | 25 | fn main() {} 26 | -------------------------------------------------------------------------------- /devtool/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use anyhow::{Context, Result}; 4 | use xshell::{Shell, cmd}; 5 | 6 | pub fn rustc_public_dir() -> PathBuf { 7 | let dev_tool_dir: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 8 | dev_tool_dir.parent().unwrap().to_path_buf() 9 | } 10 | 11 | pub fn active_toolchain() -> Result { 12 | let sh = Shell::new()?; 13 | sh.change_dir(rustc_public_dir()); 14 | let stdout = cmd!(sh, "rustup show active-toolchain").read()?; 15 | Ok(stdout.split_whitespace().next().context("Could not obtain active Rust toolchain")?.into()) 16 | } 17 | -------------------------------------------------------------------------------- /rustc_public/tests/sanity-checks/simple/simple_lib.rs: -------------------------------------------------------------------------------- 1 | //@check-pass 2 | 3 | //! Just a simple library 4 | 5 | pub struct Point { 6 | pub x: i64, 7 | pub y: i64, 8 | } 9 | 10 | impl Point { 11 | pub fn distance(&self, other: &Point) -> u64 { 12 | let (x_dist, x_over) = (self.x - other.x).overflowing_pow(2); 13 | let (y_dist, y_over) = (self.y - other.y).overflowing_pow(2); 14 | if y_over | x_over { 15 | panic!("overflow"); 16 | } 17 | 18 | let dist = (x_dist as u64 + y_dist as u64) >> 1; 19 | dist 20 | } 21 | 22 | pub fn distance_root(&self) -> u64 { 23 | self.distance(&Point { x: 0, y: 0 }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | pub fn main() { 5 | // Add rustup to the rpath in order to properly link with the correct rustc version. 6 | let rustup_home = env::var("RUSTUP_HOME").unwrap(); 7 | let toolchain = env::var("RUSTUP_TOOLCHAIN").unwrap(); 8 | let rustc_lib: PathBuf = [&rustup_home, "toolchains", &toolchain, "lib"] 9 | .iter() 10 | .collect(); 11 | 12 | // If your binary target has a different name to your package, you'll need 13 | // to hardcode it here. 14 | let bin_name = env::var("CARGO_PKG_NAME").unwrap(); 15 | 16 | println!("cargo:rustc-link-arg-bin={}=-Wl,-rpath,{}", bin_name, rustc_lib.display()); 17 | } 18 | -------------------------------------------------------------------------------- /rustc_public/src/rustc_internal/pretty.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use rustc_middle::ty::TyCtxt; 4 | 5 | use super::run; 6 | 7 | pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> { 8 | writeln!( 9 | w, 10 | "// WARNING: This is highly experimental output it's intended for rustc_public developers only." 11 | )?; 12 | writeln!( 13 | w, 14 | "// If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir." 15 | )?; 16 | let _ = run(tcx, || { 17 | let items = crate::all_local_items(); 18 | let _ = items.iter().map(|item| -> io::Result<()> { item.emit_mir(w) }).collect::>(); 19 | }); 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /CHARTER.md: -------------------------------------------------------------------------------- 1 | # Stable MIR Librarification Charter 2 | 6 | 7 | ## Goals 8 | 9 | 14 | 15 | ## Constraints And Considerations 16 | 17 | 21 | 22 | 23 | ## Membership 24 | 25 | 29 | 30 | **Shepherd:** 31 | **Team Liason:** 32 | **Members:** 33 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Run compiler tests 2 | 3 | on: 4 | schedule: 5 | - cron: "0 6 * * *" # Run daily at 06:00 UTC 6 | workflow_dispatch: # Allow manual dispatching 7 | pull_request: 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | jobs: 14 | msrv: 15 | name: MSRV Nightly Tests 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: dtolnay/rust-toolchain@master 20 | with: 21 | # Note that the downloaded version is dated 2025-08-09. 22 | toolchain: nightly-2025-08-10 23 | components: rust-src 24 | - run: ./x test 25 | 26 | latest: 27 | name: Latest Nightly Tests 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: dtolnay/rust-toolchain@nightly 32 | with: 33 | components: rust-src 34 | - run: ./x test 35 | -------------------------------------------------------------------------------- /scripts/pre-push.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Call `./x fmt --check` before git push 4 | # Copy this script to .git/hooks to activate, 5 | # and remove it from .git/hooks to deactivate. 6 | # 7 | 8 | set -Euo pipefail 9 | 10 | # Check if the push is doing anything other than deleting remote branches 11 | SKIP=true 12 | while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do 13 | if [[ "$LOCAL_REF" != "(delete)" || \ 14 | "$LOCAL_SHA" != "0000000000000000000000000000000000000000" ]]; then 15 | SKIP=false 16 | fi 17 | done 18 | 19 | if $SKIP; then 20 | echo "Skipping fmt check for branch deletion" 21 | exit 0 22 | fi 23 | 24 | ROOT_DIR="$(git rev-parse --show-toplevel)" 25 | 26 | echo "Running pre-push script $ROOT_DIR/x fmt --check" 27 | 28 | cd "$ROOT_DIR" 29 | ./x fmt --check 30 | if [ $? -ne 0 ]; then 31 | echo "You may use \`git push --no-verify\` to skip this check." 32 | exit 1 33 | fi 34 | -------------------------------------------------------------------------------- /.github/workflows/deploy_mdbook.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Book 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-book: 10 | name: Build Book 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | 18 | - name: Install mdbook 19 | run: cargo install mdbook --version "^0.4" --locked 20 | 21 | - name: Run mdbook 22 | run: mdbook build book 23 | 24 | - name: Upload book 25 | uses: actions/upload-artifact@v4 26 | with: 27 | path: book/build 28 | retention-days: "3" 29 | 30 | - name: Deploy Book 31 | uses: JamesIves/github-pages-deploy-action@v4 32 | if: ${{ github.event_name == 'push' && startsWith('refs/heads/main', github.ref) }} 33 | with: 34 | branch: gh-pages 35 | folder: book/build 36 | single-commit: true 37 | -------------------------------------------------------------------------------- /rustc_public/src/unstable/internal_cx/helpers.rs: -------------------------------------------------------------------------------- 1 | //! A set of traits that define a stable interface to rustc's internals. 2 | //! 3 | //! These traits are primarily used to clarify the behavior of different 4 | //! functions that share the same name across various contexts. 5 | 6 | use rustc_middle::ty; 7 | 8 | pub(crate) trait ExistentialProjectionHelpers<'tcx> { 9 | fn new_from_args( 10 | &self, 11 | def_id: rustc_span::def_id::DefId, 12 | args: ty::GenericArgsRef<'tcx>, 13 | term: ty::Term<'tcx>, 14 | ) -> ty::ExistentialProjection<'tcx>; 15 | } 16 | 17 | pub(crate) trait ExistentialTraitRefHelpers<'tcx> { 18 | fn new_from_args( 19 | &self, 20 | trait_def_id: rustc_span::def_id::DefId, 21 | args: ty::GenericArgsRef<'tcx>, 22 | ) -> ty::ExistentialTraitRef<'tcx>; 23 | } 24 | 25 | pub(crate) trait TraitRefHelpers<'tcx> { 26 | fn new_from_args( 27 | &self, 28 | trait_def_id: rustc_span::def_id::DefId, 29 | args: ty::GenericArgsRef<'tcx>, 30 | ) -> ty::TraitRef<'tcx>; 31 | } 32 | -------------------------------------------------------------------------------- /rustc_public/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::{self, Command}; 2 | use std::{env, str}; 3 | 4 | use rustversion; 5 | 6 | const MSRV: &str = "2025-08-09"; 7 | const SUPPORTED: bool = rustversion::cfg!(since(2025-08-09)); 8 | 9 | fn main() { 10 | if !SUPPORTED && !cfg!(feature = "rustc-build") { 11 | let current = rustc_version().unwrap_or(String::from("unknown")); 12 | eprintln!( 13 | "\nERROR: rustc_public requires rustc nightly-{MSRV} or newer\n\ 14 | current: {current}\n\ 15 | help: run `rustup update nightly`.\n" 16 | ); 17 | process::exit(1); 18 | } 19 | println!("cargo:rerun-if-changed=build.rs"); 20 | } 21 | 22 | fn rustc_version() -> Option { 23 | let rustc = env::var_os("RUSTC").unwrap_or_else(|| { 24 | eprintln!("RUSTC is not set during build script execution.\n"); 25 | process::exit(1); 26 | }); 27 | let output = Command::new(rustc).arg("--version").output().ok()?; 28 | let version = str::from_utf8(&output.stdout).ok()?; 29 | version.parse().ok() 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /rustc_public/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustc_public" 3 | version = "0.1.0-preview" 4 | authors = ["rustc_public team"] 5 | description = "Define compiler intermediate representation usable by external tools" 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/rust-lang/project-stable-mir" 8 | edition = "2024" 9 | build = "build.rs" 10 | 11 | [dependencies] 12 | # tidy-alphabetical-start 13 | scoped-tls = "1.0" 14 | serde = { version = "1.0.125", features = [ "derive" ] } 15 | tracing = "0.1" 16 | # tidy-alphabetical-end 17 | 18 | [build-dependencies] 19 | rustversion = "1" 20 | 21 | [dev-dependencies] 22 | regex = "1.5.5" 23 | ui_test = "0.30.2" 24 | 25 | [[test]] 26 | name = "compiletest" 27 | harness = false 28 | 29 | [features] 30 | # tidy-alphabetical-start 31 | # This feature should only be enabled when building `rustc_public` as part 32 | # of the Rust compiler. 33 | rustc-build = [] 34 | # Provides access to APIs that expose internals of the rust compiler. 35 | # APIs enabled by this feature are unstable. They can be removed or modified 36 | # at any point and they are not included in the crate's semantic versioning. 37 | rustc_internal = [] 38 | # tidy-alphabetical-end 39 | 40 | [package.metadata.rust-analyzer] 41 | # This crate uses #[feature(rustc_private)]. 42 | # See https://github.com/rust-analyzer/rust-analyzer/pull/7891 43 | rustc_private = true -------------------------------------------------------------------------------- /book/src/tricks.md: -------------------------------------------------------------------------------- 1 | # Tricks and tips 2 | 3 | The goal of this project is to provide an interface to the Rust compiler that can be used to empower users to build 4 | their own analysis tools. 5 | Most of these tools, however, have similar requirements that goes beyond analyzing with the Rust compiler IR. 6 | For example, most tools want to be able to analyze cargo crates, including their dependencies. 7 | 8 | In this section, we document a few tricks that we found useful while developing different Rust analysis tools. 9 | 10 | ## Storing MIR for dependencies 11 | 12 | There is a compiler flag, `-Z always-encode-mir`, that can be used for storing the MIR of all functions in the crate 13 | metadata. 14 | 15 | ## Handling the Std Library 16 | 17 | Either use `Xargo` or `cargo -Z build-std` to build a new version of the std library that includes the MIR body of 18 | all functions. 19 | 20 | You can then use the compiler `--sysroot` argument to point to the version you compiled. 21 | 22 | ## Enabling Rust Analyzer for compiler crates 23 | 24 | 1. Ensure that any crate that use rustc data structures have the following configuration in their `Cargo.toml` 25 | 26 | ```toml 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | ``` 30 | 31 | 2. Set the `rust-analyzer.rustc.source` to "discover". 32 | See [Rust Analyzer manual](https://rust-analyzer.github.io/manual.html) for more advanced options. -------------------------------------------------------------------------------- /rustc_public/tests/print/operands.rs: -------------------------------------------------------------------------------- 1 | //@ compile-flags: -Z unpretty=stable-mir --crate-type lib -C panic=abort -Zmir-opt-level=0 2 | //@ check-pass 3 | //@ only-host: x86_64 4 | //! Check how stable mir pretty printer prints different operands and abort strategy. 5 | #![allow(dead_code, unused_variables)] 6 | 7 | pub fn operands(val: u8) { 8 | let array = [val; 10]; 9 | let first = array[0]; 10 | let last = array[10 - 1]; 11 | assert_eq!(first, last); 12 | 13 | let reference = &first; 14 | let dereferenced = *reference; 15 | assert_eq!(dereferenced, first); 16 | 17 | let tuple = (first, last); 18 | let (first_again, _) = tuple; 19 | let first_again_again = tuple.0; 20 | assert_eq!(first_again, first_again_again); 21 | 22 | let length = array.len(); 23 | let size_of = std::mem::size_of_val(&length); 24 | assert_eq!(length, size_of); 25 | } 26 | 27 | pub struct Dummy { 28 | c: char, 29 | i: i32, 30 | } 31 | 32 | pub enum Ctors { 33 | Unit, 34 | StructLike { d: Dummy }, 35 | TupLike(bool), 36 | } 37 | 38 | pub fn more_operands() -> [Ctors; 3] { 39 | let dummy = Dummy { c: 'a', i: i32::MIN }; 40 | let unit = Ctors::Unit; 41 | let struct_like = Ctors::StructLike { d: dummy }; 42 | let tup_like = Ctors::TupLike(false); 43 | [unit, struct_like, tup_like] 44 | } 45 | 46 | pub fn closures(x: bool, z: bool) -> impl FnOnce(bool) -> bool { 47 | move |y: bool| (x ^ y) || z 48 | } 49 | -------------------------------------------------------------------------------- /rustc_public/src/target.rs: -------------------------------------------------------------------------------- 1 | //! Provide information about the machine that this is being compiled into. 2 | 3 | use serde::Serialize; 4 | 5 | use crate::compiler_interface::with; 6 | 7 | /// The properties of the target machine being compiled into. 8 | #[derive(Clone, PartialEq, Eq, Serialize)] 9 | pub struct MachineInfo { 10 | pub endian: Endian, 11 | pub pointer_width: MachineSize, 12 | } 13 | 14 | impl MachineInfo { 15 | pub fn target() -> MachineInfo { 16 | with(|cx| cx.target_info()) 17 | } 18 | 19 | pub fn target_endianness() -> Endian { 20 | with(|cx| cx.target_info().endian) 21 | } 22 | 23 | pub fn target_pointer_width() -> MachineSize { 24 | with(|cx| cx.target_info().pointer_width) 25 | } 26 | } 27 | 28 | #[derive(Copy, Clone, PartialEq, Eq, Serialize)] 29 | pub enum Endian { 30 | Little, 31 | Big, 32 | } 33 | 34 | /// Represent the size of a component. 35 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] 36 | pub struct MachineSize { 37 | num_bits: usize, 38 | } 39 | 40 | impl MachineSize { 41 | #[inline(always)] 42 | pub fn bytes(self) -> usize { 43 | self.num_bits / 8 44 | } 45 | 46 | #[inline(always)] 47 | pub fn bits(self) -> usize { 48 | self.num_bits 49 | } 50 | 51 | #[inline(always)] 52 | pub fn from_bits(num_bits: usize) -> MachineSize { 53 | MachineSize { num_bits } 54 | } 55 | 56 | #[inline] 57 | pub fn unsigned_int_max(self) -> Option { 58 | (self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits())) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /book/src/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | The supported APIs are currently exposed as a crate in the compiler named `rustc_public`[^release]. 4 | This crate includes the definition of structures and methods to provide a SemVer compliant interface, 5 | which are expected to become the public APIs in the compiler. 6 | 7 | These APIs were designed to provide information about a Rust crate, including the body of functions, as well as type 8 | and layout information. 9 | 10 | This chapter has two sections directed at different use cases. 11 | 12 | 1. If you already have a crate that uses some of the Rust compiler libraries, 13 | and you are interested in migrating them to the StableMIR APIs, 14 | you can find more details about your use case at the [Migrating to StableMIR](./migrating.md) section. 15 | 2. If you are starting your integration with the Rust compiler via StableMIR, we recommend reading through the 16 | [Initial Integration](./initial.md) chapter. 17 | 18 | We also include a [Tips and Tricks](./tricks.md) section that is related to a few common obstacles tool writers 19 | encounter, 20 | that is not directly related to the `rustc_public` crate and APIs. 21 | 22 | Our repository also includes a little [demo crate](https://github.com/rust-lang/project-stable-mir/tree/main/demo) that 23 | demonstrate how `rustc_public` crate can be used to analyze the main function of a crate. 24 | 25 | The latest crate documentation can be found in the 26 | [nightly documentation here](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_public/index.html) 27 | 28 | [^release]: We are planning to release the `rustc_public` crate into crates.io in the near future. 29 | See issue [#0030](https://github.com/rust-lang/project-stable-mir/issues/30) for the current release status. 30 | -------------------------------------------------------------------------------- /demo/example/methods.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_into_inner)] 2 | 3 | use std::pin::Pin; 4 | use std::rc::Rc; 5 | use std::sync::Arc; 6 | 7 | #[derive(Default, Debug)] 8 | struct Example { 9 | inner: String, 10 | } 11 | 12 | type Alias = Example; 13 | trait Trait { 14 | type Output; 15 | } 16 | impl Trait for Example { 17 | type Output = Example; 18 | } 19 | 20 | #[allow(unused)] 21 | impl Example { 22 | pub fn by_value(self: Self) { 23 | self.by_ref("by_val"); 24 | } 25 | 26 | pub fn by_ref(self: &Self, source: &str) { 27 | println!("{source}: {}", self.inner); 28 | } 29 | 30 | pub fn by_ref_mut(self: &mut Self) { 31 | self.inner = "by_ref_mut".to_string(); 32 | self.by_ref("mut"); 33 | } 34 | 35 | pub fn by_box(self: Box) { 36 | self.by_ref("by_box"); 37 | Box::into_inner(self).by_value(); 38 | } 39 | 40 | pub fn by_rc(self: Rc) { 41 | self.by_ref("by_rc"); 42 | } 43 | 44 | pub fn by_arc(self: Arc) { 45 | self.by_ref("by_arc"); 46 | } 47 | 48 | pub fn by_pin(self: Pin<&Self>) { 49 | self.by_ref("by_pin"); 50 | } 51 | 52 | pub fn explicit_type(self: Arc) { 53 | self.by_ref("explicit"); 54 | } 55 | 56 | pub fn with_lifetime<'a>(self: &'a Self) { 57 | self.by_ref("lifetime"); 58 | } 59 | 60 | pub fn nested<'a>(self: &mut &'a Arc>>) { 61 | self.by_ref("nested"); 62 | } 63 | 64 | pub fn via_projection(self: ::Output) { 65 | self.by_ref("via_projection"); 66 | } 67 | 68 | pub fn from(name: String) -> Self { 69 | Example { inner: name } 70 | } 71 | } 72 | 73 | fn main() { 74 | let example = Example::from("Hello".to_string()); 75 | example.by_value(); 76 | 77 | let boxed = Box::new(Example::default()); 78 | boxed.by_box(); 79 | 80 | Example::default().by_ref_mut(); 81 | Example::default().with_lifetime(); 82 | } 83 | -------------------------------------------------------------------------------- /rustc_public/tests/sanity-checks/associated-items/methods.rs: -------------------------------------------------------------------------------- 1 | //@check-pass 2 | 3 | //! Example derived from 4 | #![feature(box_into_inner)] 5 | 6 | use std::pin::Pin; 7 | use std::rc::Rc; 8 | use std::sync::Arc; 9 | 10 | #[derive(Default, Debug)] 11 | struct Example { 12 | inner: String, 13 | } 14 | 15 | type Alias = Example; 16 | trait Trait { 17 | type Output; 18 | } 19 | impl Trait for Example { 20 | type Output = Example; 21 | } 22 | 23 | #[allow(unused)] 24 | impl Example { 25 | pub fn by_value(self: Self) { 26 | self.by_ref("by_val"); 27 | } 28 | 29 | pub fn by_ref(self: &Self, source: &str) { 30 | println!("{source}: {}", self.inner); 31 | } 32 | 33 | pub fn by_ref_mut(self: &mut Self) { 34 | self.inner = "by_ref_mut".to_string(); 35 | self.by_ref("mut"); 36 | } 37 | 38 | pub fn by_box(self: Box) { 39 | self.by_ref("by_box"); 40 | Box::into_inner(self).by_value(); 41 | } 42 | 43 | pub fn by_rc(self: Rc) { 44 | self.by_ref("by_rc"); 45 | } 46 | 47 | pub fn by_arc(self: Arc) { 48 | self.by_ref("by_arc"); 49 | } 50 | 51 | pub fn by_pin(self: Pin<&Self>) { 52 | self.by_ref("by_pin"); 53 | } 54 | 55 | pub fn explicit_type(self: Arc) { 56 | self.by_ref("explicit"); 57 | } 58 | 59 | pub fn with_lifetime<'a>(self: &'a Self) { 60 | self.by_ref("lifetime"); 61 | } 62 | 63 | pub fn nested<'a>(self: &mut &'a Arc>>) { 64 | self.by_ref("nested"); 65 | } 66 | 67 | pub fn via_projection(self: ::Output) { 68 | self.by_ref("via_projection"); 69 | } 70 | 71 | pub fn from(name: String) -> Self { 72 | Example { inner: name } 73 | } 74 | } 75 | 76 | fn main() { 77 | let example = Example::from("Hello".to_string()); 78 | example.by_value(); 79 | 80 | let boxed = Box::new(Example::default()); 81 | boxed.by_box(); 82 | 83 | Example::default().by_ref_mut(); 84 | Example::default().with_lifetime(); 85 | } 86 | -------------------------------------------------------------------------------- /rustc_public/README.md: -------------------------------------------------------------------------------- 1 | This crate is currently developed in-tree together with the compiler. 2 | 3 | Our goal is to start publishing `rustc_public` into crates.io. 4 | Until then, users will use this as any other rustc crate, by installing 5 | the rustup component `rustc-dev`, and declaring `rustc-public` as an external crate. 6 | 7 | See the StableMIR ["Getting Started"](https://rust-lang.github.io/project-stable-mir/getting-started.html) 8 | guide for more information. 9 | 10 | ## Stable MIR Design 11 | 12 | The stable-mir will follow a similar approach to proc-macro2. Its 13 | implementation is split between two main crates: 14 | 15 | - `rustc_public`: Public crate, to be published on crates.io, which will contain 16 | the stable data structure as well as calls to `rustc_public_bridge` APIs. The 17 | translation between stable and internal constructs is also done in this crate. 18 | - `rustc_public_bridge`: This crate implements the public APIs to the compiler. 19 | It is responsible for gathering all the information requested, and providing 20 | the data in its unstable form. 21 | 22 | I.e., 23 | tools will depend on `rustc_public` crate, 24 | which will invoke the compiler using APIs defined in `rustc_public_bridge`. 25 | 26 | I.e.: 27 | 28 | ``` 29 | ┌────────────────────────────┐ ┌───────────────────────────┐ 30 | │ External Tool │ │ Rust Compiler │ 31 | │ ┌────────────┐ │ │ ┌────────┐ │ 32 | │ │ │ │ │ │ │ │ 33 | │ │rustc_public│ │ │ │rustc │ │ 34 | │ │ │ ├──────────►| │public │ │ 35 | │ │ │ │◄──────────┤ │bridge │ │ 36 | │ │ │ │ │ │ │ │ 37 | │ │ │ │ │ │ │ │ 38 | │ └────────────┘ │ │ └────────┘ │ 39 | └────────────────────────────┘ └───────────────────────────┘ 40 | ``` 41 | 42 | More details can be found here: 43 | https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustc Librarification Project Group (formerly Stable MIR) 2 | 3 | 8 | ![project group status: active](https://img.shields.io/badge/status-active-brightgreen.svg) 9 | [![project group documentation](https://img.shields.io/badge/MDBook-View%20Documentation-blue)][gh-pages] 10 | [![Run compiler tests](https://github.com/rust-lang/project-stable-mir/actions/workflows/nightly.yml/badge.svg)](https://github.com/rust-lang/project-stable-mir/actions/workflows/nightly.yml) 11 | 12 | 13 | 17 | 18 | Welcome to the repository for the Rustc Librarification Project Group! Our goal is to provide a SemVer compliant 19 | API based on the rust compiler mid-level intermediate representation (MIR) that can be used as the foundation 20 | for development of tools that want to perform sophisticated analyses and make stronger guarantees about the 21 | behavior of Rust programs. 22 | 23 | To avoid confusion, we have renamed our project and our crates to `rustc_public` to better reflect our goal of providing 24 | a public, SemVer compliant interface rather than a completely stable API. 25 | 26 | This is the repository we use to organise and document our work. 27 | 28 | If you are wondering how to use rustc_public in your project, please check out the [Getting Started][tutorial] chapter. 29 | 30 | [gh-pages]: https://rust-lang.github.io/project-stable-mir 31 | 32 | [tutorial]: https://rust-lang.github.io/project-stable-mir/getting-started.html 33 | 34 | ## How Can I Get Involved? 35 | 36 | [You can find a list of the current members available 37 | on `rust-lang/team`.][team-toml] 38 | 39 | If you'd like to participate be sure to check out any of our [open issues] on this 40 | repository. 41 | 42 | We also participate on [Zulip][chat-link], feel free to introduce 43 | yourself over there and ask us any questions you have. 44 | 45 | 46 | [open issues]: https://github.com/rust-lang/project-stable-mir/issues 47 | 48 | [chat-link]: https://rust-lang.zulipchat.com/#narrow/stream/320896-project-stable-mir 49 | 50 | [team-toml]: https://github.com/rust-lang/team/blob/master/teams/project-stable-mir.toml 51 | -------------------------------------------------------------------------------- /rustc_public/tests/print/basic_function.stdout: -------------------------------------------------------------------------------- 1 | // WARNING: This is highly experimental output it's intended for rustc_public developers only. 2 | // If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir. 3 | fn foo(_1: i32) -> i32 { 4 | let mut _0: i32; 5 | let mut _2: i32; 6 | let mut _3: (i32, bool); 7 | debug i => _1; 8 | bb0: { 9 | StorageLive(_2); 10 | _2 = _1; 11 | _3 = CheckedAdd(_2, 1_i32); 12 | assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, 1_i32) -> [success: bb1, unwind continue]; 13 | } 14 | bb1: { 15 | _0 = move (_3.0: i32); 16 | StorageDead(_2); 17 | return; 18 | } 19 | } 20 | fn bar(_1: &mut std::vec::Vec) -> std::vec::Vec { 21 | let mut _0: std::vec::Vec; 22 | let mut _2: std::vec::Vec; 23 | let mut _3: &std::vec::Vec; 24 | let _4: (); 25 | let mut _5: &mut std::vec::Vec; 26 | debug vec => _1; 27 | debug new_vec => _2; 28 | bb0: { 29 | StorageLive(_2); 30 | StorageLive(_3); 31 | _3 = &(*_1); 32 | _2 = as std::clone::Clone>::clone(move _3) -> [return: bb1, unwind continue]; 33 | } 34 | bb1: { 35 | StorageDead(_3); 36 | StorageLive(_4); 37 | StorageLive(_5); 38 | _5 = &mut _2; 39 | _4 = std::vec::Vec::::push(move _5, 1_i32) -> [return: bb2, unwind: bb3]; 40 | } 41 | bb2: { 42 | StorageDead(_5); 43 | StorageDead(_4); 44 | _0 = move _2; 45 | StorageDead(_2); 46 | return; 47 | } 48 | bb3: { 49 | drop(_2) -> [return: bb4, unwind terminate]; 50 | } 51 | bb4: { 52 | resume; 53 | } 54 | } 55 | fn demux(_1: u8) -> u8 { 56 | let mut _0: u8; 57 | debug input => _1; 58 | bb0: { 59 | switchInt(_1) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1]; 60 | } 61 | bb1: { 62 | _0 = 0_u8; 63 | goto -> bb5; 64 | } 65 | bb2: { 66 | _0 = 8_u8; 67 | goto -> bb5; 68 | } 69 | bb3: { 70 | _0 = 6_u8; 71 | goto -> bb5; 72 | } 73 | bb4: { 74 | _0 = 10_u8; 75 | goto -> bb5; 76 | } 77 | bb5: { 78 | return; 79 | } 80 | } 81 | fn main() -> () { 82 | let mut _0: (); 83 | bb0: { 84 | _0 = (); 85 | return; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rustc_public/src/error.rs: -------------------------------------------------------------------------------- 1 | //! When things go wrong, we need some error handling. 2 | //! There are a few different types of errors in rustc_public: 3 | //! 4 | //! - [CompilerError]: This represents errors that can be raised when invoking the compiler. 5 | //! - [Error]: Generic error that represents the reason why a request that could not be fulfilled. 6 | 7 | use std::fmt::{Debug, Display, Formatter}; 8 | use std::{fmt, io}; 9 | 10 | use rustc_public_bridge::bridge; 11 | 12 | macro_rules! error { 13 | ($fmt: literal $(,)?) => { Error(format!($fmt)) }; 14 | ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) }; 15 | } 16 | 17 | pub(crate) use error; 18 | 19 | /// An error type used to represent an error that has already been reported by the compiler. 20 | #[derive(Clone, Copy, PartialEq, Eq)] 21 | pub enum CompilerError { 22 | /// Compilation failed, either due to normal errors or ICE. 23 | Failed, 24 | /// Compilation was interrupted. 25 | Interrupted(T), 26 | /// Compilation skipped. This happens when users invoke rustc to retrieve information such as 27 | /// --version. 28 | Skipped, 29 | } 30 | 31 | /// A generic error to represent an API request that cannot be fulfilled. 32 | #[derive(Clone, Debug, Eq, PartialEq)] 33 | pub struct Error(pub(crate) String); 34 | 35 | impl bridge::Error for Error { 36 | fn new(msg: String) -> Self { 37 | Self(msg) 38 | } 39 | 40 | fn from_internal(err: T) -> Self { 41 | Self(format!("{err:?}")) 42 | } 43 | } 44 | 45 | impl From<&str> for Error { 46 | fn from(value: &str) -> Self { 47 | Self(value.into()) 48 | } 49 | } 50 | 51 | impl Display for Error { 52 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 53 | Display::fmt(&self.0, f) 54 | } 55 | } 56 | 57 | impl Display for CompilerError 58 | where 59 | T: Display, 60 | { 61 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 62 | match self { 63 | CompilerError::Failed => write!(f, "Compilation Failed"), 64 | CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"), 65 | CompilerError::Skipped => write!(f, "Compilation Skipped"), 66 | } 67 | } 68 | } 69 | 70 | impl Debug for CompilerError 71 | where 72 | T: Debug, 73 | { 74 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 75 | match self { 76 | CompilerError::Failed => write!(f, "Compilation Failed"), 77 | CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"), 78 | CompilerError::Skipped => write!(f, "Compilation Skipped"), 79 | } 80 | } 81 | } 82 | 83 | impl std::error::Error for Error {} 84 | 85 | impl std::error::Error for CompilerError where T: Display + Debug {} 86 | 87 | impl From for Error { 88 | fn from(value: io::Error) -> Self { 89 | Error(value.to_string()) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /rustc_public/src/unstable/internal_cx/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of InternalCx. 2 | 3 | pub(crate) use helpers::*; 4 | use rustc_middle::ty::{List, Ty, TyCtxt}; 5 | use rustc_middle::{mir, ty}; 6 | 7 | use super::InternalCx; 8 | 9 | pub(crate) mod helpers; 10 | 11 | impl<'tcx, T: InternalCx<'tcx>> ExistentialProjectionHelpers<'tcx> for T { 12 | fn new_from_args( 13 | &self, 14 | def_id: rustc_span::def_id::DefId, 15 | args: ty::GenericArgsRef<'tcx>, 16 | term: ty::Term<'tcx>, 17 | ) -> ty::ExistentialProjection<'tcx> { 18 | ty::ExistentialProjection::new_from_args(self.tcx(), def_id, args, term) 19 | } 20 | } 21 | 22 | impl<'tcx, T: InternalCx<'tcx>> ExistentialTraitRefHelpers<'tcx> for T { 23 | fn new_from_args( 24 | &self, 25 | trait_def_id: rustc_span::def_id::DefId, 26 | args: ty::GenericArgsRef<'tcx>, 27 | ) -> ty::ExistentialTraitRef<'tcx> { 28 | ty::ExistentialTraitRef::new_from_args(self.tcx(), trait_def_id, args) 29 | } 30 | } 31 | 32 | impl<'tcx, T: InternalCx<'tcx>> TraitRefHelpers<'tcx> for T { 33 | fn new_from_args( 34 | &self, 35 | trait_def_id: rustc_span::def_id::DefId, 36 | args: ty::GenericArgsRef<'tcx>, 37 | ) -> ty::TraitRef<'tcx> { 38 | ty::TraitRef::new_from_args(self.tcx(), trait_def_id, args) 39 | } 40 | } 41 | 42 | impl<'tcx> InternalCx<'tcx> for TyCtxt<'tcx> { 43 | fn tcx(self) -> TyCtxt<'tcx> { 44 | self 45 | } 46 | 47 | fn lift>>(self, value: T) -> Option { 48 | TyCtxt::lift(self, value) 49 | } 50 | 51 | fn mk_args_from_iter(self, iter: I) -> T::Output 52 | where 53 | I: Iterator, 54 | T: ty::CollectAndApply, ty::GenericArgsRef<'tcx>>, 55 | { 56 | TyCtxt::mk_args_from_iter(self, iter) 57 | } 58 | 59 | fn mk_pat(self, v: ty::PatternKind<'tcx>) -> ty::Pattern<'tcx> { 60 | TyCtxt::mk_pat(self, v) 61 | } 62 | 63 | fn mk_poly_existential_predicates( 64 | self, 65 | eps: &[ty::PolyExistentialPredicate<'tcx>], 66 | ) -> &'tcx List> { 67 | TyCtxt::mk_poly_existential_predicates(self, eps) 68 | } 69 | 70 | fn mk_type_list(self, v: &[Ty<'tcx>]) -> &'tcx List> { 71 | TyCtxt::mk_type_list(self, v) 72 | } 73 | 74 | fn lifetimes_re_erased(self) -> ty::Region<'tcx> { 75 | self.lifetimes.re_erased 76 | } 77 | 78 | fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output 79 | where 80 | I: Iterator, 81 | T: ty::CollectAndApply>, 82 | { 83 | TyCtxt::mk_bound_variable_kinds_from_iter(self, iter) 84 | } 85 | 86 | fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List> { 87 | TyCtxt::mk_place_elems(self, v) 88 | } 89 | 90 | fn adt_def(self, def_id: rustc_hir::def_id::DefId) -> ty::AdtDef<'tcx> { 91 | self.adt_def(def_id) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /demo/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Small utility that print some information about a crate. 2 | 3 | #![feature(rustc_private)] 4 | #![feature(assert_matches)] 5 | 6 | extern crate rustc_driver; 7 | extern crate rustc_interface; 8 | extern crate rustc_middle; 9 | extern crate rustc_public; 10 | 11 | use std::collections::HashSet; 12 | use std::io::stdout; 13 | use rustc_public::run; 14 | use rustc_public::{CompilerError, CrateDef}; 15 | use std::ops::ControlFlow; 16 | use std::process::ExitCode; 17 | use rustc_public::mir::{LocalDecl, MirVisitor, Terminator, TerminatorKind}; 18 | use rustc_public::mir::mono::Instance; 19 | use rustc_public::mir::visit::Location; 20 | use rustc_public::ty::{RigidTy, Ty, TyKind}; 21 | 22 | 23 | /// This is a wrapper that can be used to replace rustc. 24 | fn main() -> ExitCode { 25 | let rustc_args: Vec = std::env::args().collect(); 26 | let result = run!(&rustc_args, start_demo); 27 | match result { 28 | Ok(_) | Err(CompilerError::Skipped | CompilerError::Interrupted(_)) => ExitCode::SUCCESS, 29 | _ => ExitCode::FAILURE, 30 | } 31 | } 32 | 33 | fn start_demo() -> ControlFlow<()> { 34 | let crate_name = rustc_public::local_crate().name; 35 | eprintln!("--- Analyzing crate: {crate_name}"); 36 | 37 | let crate_items = rustc_public::all_local_items(); 38 | for item in crate_items { 39 | eprintln!(" - {} @{:?}", item.name(), item.span()) 40 | } 41 | 42 | let entry_fn = rustc_public::entry_fn().unwrap(); 43 | let entry_instance = Instance::try_from(entry_fn).unwrap(); 44 | analyze_instance(entry_instance); 45 | ControlFlow::Break(()) 46 | } 47 | 48 | fn analyze_instance(instance: Instance) { 49 | eprintln!("--- Analyzing instance: {}", instance.name()); 50 | eprintln!(" - Mangled name: {}", instance.mangled_name()); 51 | eprintln!(" - FnABI: {:?}", instance.fn_abi().unwrap()); 52 | 53 | let body = instance.body().unwrap(); 54 | let mut visitor = Visitor { 55 | locals: body.locals(), 56 | tys: Default::default(), 57 | fn_calls: Default::default(), 58 | }; 59 | visitor.visit_body(&body); 60 | visitor.tys.iter().for_each(|ty| eprintln!(" - Visited: {ty}")); 61 | visitor.fn_calls.iter().for_each(|instance| eprintln!(" - Call: {}", instance.name())); 62 | 63 | body.dump(&mut stdout().lock(), &instance.name()).unwrap(); 64 | } 65 | 66 | struct Visitor<'a> { 67 | locals: &'a [LocalDecl], 68 | tys: HashSet, 69 | fn_calls: HashSet, 70 | } 71 | 72 | impl<'a> MirVisitor for Visitor<'a> { 73 | fn visit_terminator(&mut self, term: &Terminator, _location: Location) { 74 | match term.kind { 75 | TerminatorKind::Call { ref func, .. } => { 76 | let op_ty = func.ty(self.locals).unwrap(); 77 | let TyKind::RigidTy(RigidTy::FnDef(def, args)) = op_ty.kind() else { return; }; 78 | self.fn_calls.insert(Instance::resolve(def, &args).unwrap()); 79 | } 80 | _ => {} 81 | } 82 | } 83 | 84 | fn visit_ty(&mut self, ty: &Ty, _location: Location) { 85 | self.tys.insert(*ty); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rustc_public/src/unstable/convert/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module holds the logic to convert rustc internal ADTs into rustc_public ADTs. 2 | //! 3 | //! The conversion from stable to internal is not meant to be complete, 4 | //! and it should be added as when needed to be passed as input to rustc_public_bridge functions. 5 | //! 6 | //! For contributors, please make sure to avoid calling rustc's internal functions and queries. 7 | //! These should be done via `rustc_public_bridge` APIs, but it's possible to access ADT fields directly. 8 | 9 | use std::ops::RangeInclusive; 10 | 11 | use rustc_public_bridge::Tables; 12 | use rustc_public_bridge::context::CompilerCtxt; 13 | 14 | use super::Stable; 15 | use crate::compiler_interface::BridgeTys; 16 | 17 | mod internal; 18 | mod stable; 19 | 20 | impl<'tcx, T> Stable<'tcx> for &T 21 | where 22 | T: Stable<'tcx>, 23 | { 24 | type T = T::T; 25 | 26 | fn stable<'cx>( 27 | &self, 28 | tables: &mut Tables<'cx, BridgeTys>, 29 | cx: &CompilerCtxt<'cx, BridgeTys>, 30 | ) -> Self::T { 31 | (*self).stable(tables, cx) 32 | } 33 | } 34 | 35 | impl<'tcx, T> Stable<'tcx> for Option 36 | where 37 | T: Stable<'tcx>, 38 | { 39 | type T = Option; 40 | 41 | fn stable<'cx>( 42 | &self, 43 | tables: &mut Tables<'cx, BridgeTys>, 44 | cx: &CompilerCtxt<'cx, BridgeTys>, 45 | ) -> Self::T { 46 | self.as_ref().map(|value| value.stable(tables, cx)) 47 | } 48 | } 49 | 50 | impl<'tcx, T, E> Stable<'tcx> for Result 51 | where 52 | T: Stable<'tcx>, 53 | E: Stable<'tcx>, 54 | { 55 | type T = Result; 56 | 57 | fn stable<'cx>( 58 | &self, 59 | tables: &mut Tables<'cx, BridgeTys>, 60 | cx: &CompilerCtxt<'cx, BridgeTys>, 61 | ) -> Self::T { 62 | match self { 63 | Ok(val) => Ok(val.stable(tables, cx)), 64 | Err(error) => Err(error.stable(tables, cx)), 65 | } 66 | } 67 | } 68 | 69 | impl<'tcx, T> Stable<'tcx> for &[T] 70 | where 71 | T: Stable<'tcx>, 72 | { 73 | type T = Vec; 74 | fn stable<'cx>( 75 | &self, 76 | tables: &mut Tables<'cx, BridgeTys>, 77 | cx: &CompilerCtxt<'cx, BridgeTys>, 78 | ) -> Self::T { 79 | self.iter().map(|e| e.stable(tables, cx)).collect() 80 | } 81 | } 82 | 83 | impl<'tcx, T, U> Stable<'tcx> for (T, U) 84 | where 85 | T: Stable<'tcx>, 86 | U: Stable<'tcx>, 87 | { 88 | type T = (T::T, U::T); 89 | fn stable<'cx>( 90 | &self, 91 | tables: &mut Tables<'cx, BridgeTys>, 92 | cx: &CompilerCtxt<'cx, BridgeTys>, 93 | ) -> Self::T { 94 | (self.0.stable(tables, cx), self.1.stable(tables, cx)) 95 | } 96 | } 97 | 98 | impl<'tcx, T> Stable<'tcx> for RangeInclusive 99 | where 100 | T: Stable<'tcx>, 101 | { 102 | type T = RangeInclusive; 103 | fn stable<'cx>( 104 | &self, 105 | tables: &mut Tables<'cx, BridgeTys>, 106 | cx: &CompilerCtxt<'cx, BridgeTys>, 107 | ) -> Self::T { 108 | RangeInclusive::new(self.start().stable(tables, cx), self.end().stable(tables, cx)) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /rustc_public/src/alloc.rs: -------------------------------------------------------------------------------- 1 | //! Memory allocation implementation for rustc_public. 2 | //! 3 | //! This module is responsible for constructing stable components. 4 | //! All operations requiring rustc queries must be delegated 5 | //! to `rustc_public_bridge::alloc` to maintain stability guarantees. 6 | 7 | use rustc_abi::Align; 8 | use rustc_middle::mir::ConstValue; 9 | use rustc_middle::mir::interpret::AllocRange; 10 | use rustc_public_bridge::bridge::Error as _; 11 | use rustc_public_bridge::context::CompilerCtxt; 12 | use rustc_public_bridge::{Tables, alloc}; 13 | 14 | use super::Error; 15 | use super::compiler_interface::BridgeTys; 16 | use super::mir::Mutability; 17 | use super::ty::{Allocation, ProvenanceMap}; 18 | use super::unstable::Stable; 19 | 20 | /// Creates new empty `Allocation` from given `Align`. 21 | fn new_empty_allocation(align: Align) -> Allocation { 22 | Allocation { 23 | bytes: Vec::new(), 24 | provenance: ProvenanceMap { ptrs: Vec::new() }, 25 | align: align.bytes(), 26 | mutability: Mutability::Not, 27 | } 28 | } 29 | 30 | // We need this method instead of a Stable implementation 31 | // because we need to get `Ty` of the const we are trying to create, to do that 32 | // we need to have access to `ConstantKind` but we can't access that inside Stable impl. 33 | #[allow(rustc::usage_of_qualified_ty)] 34 | pub(crate) fn new_allocation<'tcx>( 35 | ty: rustc_middle::ty::Ty<'tcx>, 36 | const_value: ConstValue, 37 | tables: &mut Tables<'tcx, BridgeTys>, 38 | cx: &CompilerCtxt<'tcx, BridgeTys>, 39 | ) -> Allocation { 40 | try_new_allocation(ty, const_value, tables, cx) 41 | .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}")) 42 | } 43 | 44 | #[allow(rustc::usage_of_qualified_ty)] 45 | pub(crate) fn try_new_allocation<'tcx>( 46 | ty: rustc_middle::ty::Ty<'tcx>, 47 | const_value: ConstValue, 48 | tables: &mut Tables<'tcx, BridgeTys>, 49 | cx: &CompilerCtxt<'tcx, BridgeTys>, 50 | ) -> Result { 51 | let layout = alloc::create_ty_and_layout(cx, ty).map_err(|e| Error::from_internal(e))?; 52 | match const_value { 53 | ConstValue::Scalar(scalar) => { 54 | alloc::try_new_scalar(layout, scalar, cx).map(|alloc| alloc.stable(tables, cx)) 55 | } 56 | ConstValue::ZeroSized => Ok(new_empty_allocation(layout.align.abi)), 57 | ConstValue::Slice { alloc_id, meta } => { 58 | alloc::try_new_slice(layout, alloc_id, meta, cx).map(|alloc| alloc.stable(tables, cx)) 59 | } 60 | ConstValue::Indirect { alloc_id, offset } => { 61 | let alloc = alloc::try_new_indirect(alloc_id, cx); 62 | use rustc_public_bridge::context::AllocRangeHelpers; 63 | Ok(allocation_filter(&alloc.0, cx.alloc_range(offset, layout.size), tables, cx)) 64 | } 65 | } 66 | } 67 | 68 | /// Creates an `Allocation` only from information within the `AllocRange`. 69 | pub(super) fn allocation_filter<'tcx>( 70 | alloc: &rustc_middle::mir::interpret::Allocation, 71 | alloc_range: AllocRange, 72 | tables: &mut Tables<'tcx, BridgeTys>, 73 | cx: &CompilerCtxt<'tcx, BridgeTys>, 74 | ) -> Allocation { 75 | alloc::allocation_filter(alloc, alloc_range, tables, cx) 76 | } 77 | -------------------------------------------------------------------------------- /rustc_public/src/unstable/convert/stable/mod.rs: -------------------------------------------------------------------------------- 1 | //! Conversion of internal Rust compiler items to stable ones. 2 | 3 | use rustc_abi::FieldIdx; 4 | use rustc_public_bridge::Tables; 5 | use rustc_public_bridge::context::CompilerCtxt; 6 | 7 | use super::Stable; 8 | use crate::compiler_interface::BridgeTys; 9 | 10 | mod abi; 11 | mod mir; 12 | mod ty; 13 | 14 | impl<'tcx> Stable<'tcx> for rustc_hir::Safety { 15 | type T = crate::mir::Safety; 16 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 17 | match self { 18 | rustc_hir::Safety::Unsafe => crate::mir::Safety::Unsafe, 19 | rustc_hir::Safety::Safe => crate::mir::Safety::Safe, 20 | } 21 | } 22 | } 23 | 24 | impl<'tcx> Stable<'tcx> for FieldIdx { 25 | type T = usize; 26 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 27 | self.as_usize() 28 | } 29 | } 30 | 31 | impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource { 32 | type T = crate::mir::CoroutineSource; 33 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 34 | use rustc_hir::CoroutineSource; 35 | match self { 36 | CoroutineSource::Block => crate::mir::CoroutineSource::Block, 37 | CoroutineSource::Closure => crate::mir::CoroutineSource::Closure, 38 | CoroutineSource::Fn => crate::mir::CoroutineSource::Fn, 39 | } 40 | } 41 | } 42 | 43 | impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind { 44 | type T = crate::mir::CoroutineKind; 45 | fn stable<'cx>( 46 | &self, 47 | tables: &mut Tables<'cx, BridgeTys>, 48 | cx: &CompilerCtxt<'cx, BridgeTys>, 49 | ) -> Self::T { 50 | use rustc_hir::{CoroutineDesugaring, CoroutineKind}; 51 | match *self { 52 | CoroutineKind::Desugared(CoroutineDesugaring::Async, source) => { 53 | crate::mir::CoroutineKind::Desugared( 54 | crate::mir::CoroutineDesugaring::Async, 55 | source.stable(tables, cx), 56 | ) 57 | } 58 | CoroutineKind::Desugared(CoroutineDesugaring::Gen, source) => { 59 | crate::mir::CoroutineKind::Desugared( 60 | crate::mir::CoroutineDesugaring::Gen, 61 | source.stable(tables, cx), 62 | ) 63 | } 64 | CoroutineKind::Coroutine(movability) => { 65 | crate::mir::CoroutineKind::Coroutine(movability.stable(tables, cx)) 66 | } 67 | CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, source) => { 68 | crate::mir::CoroutineKind::Desugared( 69 | crate::mir::CoroutineDesugaring::AsyncGen, 70 | source.stable(tables, cx), 71 | ) 72 | } 73 | } 74 | } 75 | } 76 | 77 | impl<'tcx> Stable<'tcx> for rustc_span::Symbol { 78 | type T = crate::Symbol; 79 | 80 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 81 | self.to_string() 82 | } 83 | } 84 | 85 | impl<'tcx> Stable<'tcx> for rustc_span::Span { 86 | type T = crate::ty::Span; 87 | 88 | fn stable<'cx>( 89 | &self, 90 | tables: &mut Tables<'cx, BridgeTys>, 91 | _: &CompilerCtxt<'cx, BridgeTys>, 92 | ) -> Self::T { 93 | tables.create_span(*self) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /rustc_public/src/mir/alloc.rs: -------------------------------------------------------------------------------- 1 | //! This module provides methods to retrieve allocation information, such as static variables. 2 | 3 | use std::io::Read; 4 | 5 | use serde::Serialize; 6 | 7 | use crate::mir::mono::{Instance, StaticDef}; 8 | use crate::target::{Endian, MachineInfo}; 9 | use crate::ty::{Allocation, Binder, ExistentialTraitRef, Ty}; 10 | use crate::{Error, IndexedVal, with}; 11 | 12 | /// An allocation in the rustc_public's IR global memory can be either a function pointer, 13 | /// a static, or a "real" allocation with some data in it. 14 | #[derive(Debug, Clone, Eq, PartialEq, Serialize)] 15 | pub enum GlobalAlloc { 16 | /// The alloc ID is used as a function pointer. 17 | Function(Instance), 18 | /// This alloc ID points to a symbolic (not-reified) vtable. 19 | /// The `None` trait ref is used to represent auto traits. 20 | VTable(Ty, Option>), 21 | /// The alloc ID points to a "lazy" static variable that did not get computed (yet). 22 | /// This is also used to break the cycle in recursive statics. 23 | Static(StaticDef), 24 | /// The alloc ID points to memory. 25 | Memory(Allocation), 26 | /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id 27 | /// is split into two segments, on 32 bit systems there are 4 segments, and so on. 28 | TypeId { ty: Ty }, 29 | } 30 | 31 | impl From for GlobalAlloc { 32 | fn from(value: AllocId) -> Self { 33 | with(|cx| cx.global_alloc(value)) 34 | } 35 | } 36 | 37 | impl GlobalAlloc { 38 | /// Retrieve the allocation id for a global allocation if it exists. 39 | /// 40 | /// For `[GlobalAlloc::VTable]`, this will return the allocation for the VTable of the given 41 | /// type for the optional trait if the type implements the trait. 42 | /// 43 | /// This method will always return `None` for allocations other than `[GlobalAlloc::VTable]`. 44 | pub fn vtable_allocation(&self) -> Option { 45 | with(|cx| cx.vtable_allocation(self)) 46 | } 47 | } 48 | 49 | /// A unique identification number for each provenance 50 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] 51 | pub struct AllocId(usize); 52 | 53 | impl IndexedVal for AllocId { 54 | fn to_val(index: usize) -> Self { 55 | AllocId(index) 56 | } 57 | fn to_index(&self) -> usize { 58 | self.0 59 | } 60 | } 61 | 62 | /// Utility function used to read an allocation data into a unassigned integer. 63 | pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { 64 | let mut buf = [0u8; size_of::()]; 65 | match MachineInfo::target_endianness() { 66 | Endian::Little => { 67 | bytes.read_exact(&mut buf[..bytes.len()])?; 68 | Ok(u128::from_le_bytes(buf)) 69 | } 70 | Endian::Big => { 71 | bytes.read_exact(&mut buf[16 - bytes.len()..])?; 72 | Ok(u128::from_be_bytes(buf)) 73 | } 74 | } 75 | } 76 | 77 | /// Utility function used to read an allocation data into an assigned integer. 78 | pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result { 79 | let mut buf = [0u8; size_of::()]; 80 | match MachineInfo::target_endianness() { 81 | Endian::Little => { 82 | bytes.read_exact(&mut buf[..bytes.len()])?; 83 | Ok(i128::from_le_bytes(buf)) 84 | } 85 | Endian::Big => { 86 | bytes.read_exact(&mut buf[16 - bytes.len()..])?; 87 | Ok(i128::from_be_bytes(buf)) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test-drive/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Test that users are able to inspec the MIR body of functions and types 2 | 3 | #![feature(rustc_private)] 4 | #![feature(assert_matches)] 5 | 6 | mod sanity_checks; 7 | 8 | extern crate rustc_driver; 9 | extern crate rustc_interface; 10 | extern crate rustc_middle; 11 | 12 | use std::ops::ControlFlow; 13 | use std::panic::{AssertUnwindSafe, catch_unwind}; 14 | use std::process::ExitCode; 15 | use std::sync::atomic::{AtomicBool, Ordering}; 16 | 17 | use rustc_public::{CompilerError, run}; 18 | 19 | // ---- Arguments that should be parsed by the test-driver (w/ "smir" prefix) 20 | const CHECK_ARG: &str = "--smir-check"; 21 | /// Enable verbose mode. 22 | const VERBOSE_ARG: &str = "--smir-verbose"; 23 | /// Argument used to enable checks that may be failing due to an existing issue. 24 | const FIXME_ARG: &str = "--smir-fixme"; 25 | 26 | // Use a static variable for simplicity. 27 | static VERBOSE: AtomicBool = AtomicBool::new(false); 28 | static FIXME_CHECKS: AtomicBool = AtomicBool::new(false); 29 | 30 | type TestResult = Result<(), String>; 31 | 32 | /// This is a wrapper that can be used to replace rustc. 33 | /// 34 | /// Besides all supported rustc arguments, use `--check-smir` to run all the stable-mir checks. 35 | /// This allows us to use this tool in cargo projects to analyze the target crate only by running 36 | /// `cargo rustc --check-smir`. 37 | fn main() -> ExitCode { 38 | let args = std::env::args(); 39 | let (smir_args, rustc_args): (Vec, _) = args.partition(|arg| arg.starts_with("--smir")); 40 | let result = if smir_args.contains(&CHECK_ARG.to_string()) { 41 | VERBOSE.store(smir_args.contains(&VERBOSE_ARG.to_string()), Ordering::Relaxed); 42 | FIXME_CHECKS.store(smir_args.contains(&FIXME_ARG.to_string()), Ordering::Relaxed); 43 | run!(&rustc_args, test_stable_mir) 44 | } else { 45 | run!(&rustc_args, || ControlFlow::<()>::Continue(())) 46 | }; 47 | if result.is_ok() || matches!(result, Err(CompilerError::Skipped)) { 48 | ExitCode::SUCCESS 49 | } else { 50 | ExitCode::FAILURE 51 | } 52 | } 53 | 54 | macro_rules! run_tests { 55 | ($( $test:path ),+ $(,)?) => { 56 | [$({ 57 | run_test(stringify!($test), || { $test() }) 58 | },)+] 59 | }; 60 | } 61 | 62 | fn info(msg: String) { 63 | if VERBOSE.load(Ordering::Relaxed) { 64 | // We filter output based on [T-DRIVE] prefix. 65 | eprintln!("[T-DRIVE] {}", msg); 66 | } 67 | } 68 | 69 | /// This function invoke other tests and process their results. 70 | /// Tests should avoid panic, 71 | fn test_stable_mir() -> ControlFlow<()> { 72 | let mut results = Vec::from(run_tests![ 73 | sanity_checks::test_entry_fn, 74 | sanity_checks::test_all_fns, 75 | sanity_checks::test_crates, 76 | sanity_checks::test_instances, 77 | ]); 78 | if FIXME_CHECKS.load(Ordering::Relaxed) { 79 | results.extend_from_slice(&run_tests!(sanity_checks::test_traits)) 80 | } 81 | let (success, failure): (Vec<_>, Vec<_>) = results.iter().partition(|r| r.is_ok()); 82 | info(format!( 83 | "Ran {} tests. {} succeeded. {} failed", 84 | results.len(), 85 | success.len(), 86 | failure.len() 87 | )); 88 | if failure.is_empty() { ControlFlow::<()>::Continue(()) } else { ControlFlow::<()>::Break(()) } 89 | } 90 | 91 | fn run_test TestResult>(name: &str, f: F) -> TestResult { 92 | let result = match catch_unwind(AssertUnwindSafe(f)) { 93 | Err(_) => Err("Panic!".to_string()), 94 | Ok(result) => result, 95 | }; 96 | if let Err(ref msg) = result { 97 | eprintln!("Test {}: Failed:\n - {}", name, msg); 98 | } else { 99 | info(format!("Test {}: Ok", name,)); 100 | } 101 | result 102 | } 103 | -------------------------------------------------------------------------------- /book/src/initial.md: -------------------------------------------------------------------------------- 1 | # Initial Integration 2 | 3 | For an example of how to use `rustc_public`, see the [`demo/`] directory of the project-stable-mir repo. 4 | For a tutorial, read on! 5 | 6 | In order to use `rustc_public` in your crate, you will need to do the following: 7 | 8 | 1. Use a nightly toolchain that includes the `rustc_public` crate. 9 | 2. Install at least the following rustup components: "rustc-dev" and "llvm-tools" 10 | 3. Declare `rustc_public` as an external crate at the top of your crate: 11 | 12 | ```rust 13 | #![feature(rustc_private)] 14 | extern crate rustc_public; 15 | ``` 16 | 17 | For 1 and 2, we highly recommend adding a "rust-toolchain.toml" file to your project. 18 | We also recommend to pin down a specific nightly version, to ensure all users and tests are using the same compiler 19 | version. 20 | Therefore, the same `rustc_public` crate version. E.g.: 21 | 22 | ```toml 23 | # Example of a rust-toolchain.toml 24 | [toolchain] 25 | # Update the date to change the toolchain version. 26 | channel = "nightly-2025-10-27" 27 | components = ["llvm-tools", "rustc-dev", "rust-src"] 28 | ``` 29 | 30 | ## Initializing StableMIR 31 | 32 | There's currently no stable way to initialize the Rust compiler and StableMIR. 33 | See [#0069](https://github.com/rust-lang/project-stable-mir/issues/69) for more details. 34 | 35 | Instead, StableMIR includes two unstable workarounds to give you a quick start. 36 | The `run` and `run_with_tcx` macros, both from present in the `rustc_public` crate. 37 | 38 | In order to use the `run` macro, you first need to declare the following external crates: 39 | 40 | ```rust 41 | #![feature(rustc_private)] 42 | extern crate rustc_public; 43 | 44 | extern crate rustc_driver; 45 | extern crate rustc_interface; 46 | extern crate rustc_middle; 47 | ``` 48 | 49 | Then start the driver using the `run!()` macro: 50 | 51 | ```rust 52 | let result = rustc_public::run!(rustc_args, callback_fn); 53 | ``` 54 | 55 | This macro takes two arguments: 56 | 57 | 1. A `&[String]` with the command line arguments to the compiler. 58 | 2. A callback function, which can either be a closure expression or a function ident. 59 | - This callback function shouldn't take any argument, and it should return a `ControlFlow`. 60 | 61 | It will trigger the compilation in the current process, with the provided arguments, and invoke the callback after the 62 | compiler ran all its analyses, but before code generation. 63 | 64 | The macro will return a `Result>` representing the compilation result and the callback return value. 65 | 66 | The second option is the `run_with_tcx!()` macro, which is very similar to the `run` macro. 67 | The main difference is that this macro passes a copy of the Rust compiler context (`TyCtxt`) to the callback, 68 | which allows the user to also make calls to internal compiler APIs. 69 | 70 | ## Scope of StableMIR objects 71 | 72 | StableMIR objects should not be used outside the scope of the callback function. 73 | Any usage outside this scope can panic or return an incorrect value. 74 | 75 | For example, the following code is valid, since the logic we are storing is only used while the callback function 76 | is running: 77 | 78 | ```rust 79 | fn print_items(rustc_args: Vec) { 80 | let _result = run!(rustc_args, || { 81 | for item in rustc_public::all_local_items() { 82 | // Using items inside the callback! 83 | println!(" - {}", item.name()) 84 | } 85 | }); 86 | } 87 | ``` 88 | 89 | However, the following usage isn't valid, and `rustc_public` will panic when we invoke the `name()` function. 90 | 91 | ```rust 92 | fn broken_print_items(rustc_args: Vec) { 93 | let result = run!(rustc_args, || { ControlFlow::Continue(rustc_public::all_local_items())}); 94 | if let ControlFlow::Continue(items) = result { 95 | for item in items { 96 | // Using item outside the callback function is wrong! 97 | println!(" - {}", item.name()) 98 | } 99 | } 100 | } 101 | ``` 102 | 103 | StableMIR objects should also not be shared across different threads. 104 | 105 | ## Analyzing crate definitions 106 | 107 | TODO 108 | 109 | ## Analyzing monomorphic instances 110 | 111 | TODO 112 | 113 | [`demo/`](https://github.com/rust-lang/project-stable-mir/tree/main/demo) 114 | -------------------------------------------------------------------------------- /rustc_public/tests/print/async-closure.stdout: -------------------------------------------------------------------------------- 1 | // WARNING: This is highly experimental output it's intended for rustc_public developers only. 2 | // If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir. 3 | fn foo() -> () { 4 | let mut _0: (); 5 | let _1: i32; 6 | let _2: {async closure@$DIR/async-closure.rs:9:13: 9:21}; 7 | let mut _3: &i32; 8 | debug y => _1; 9 | debug x => _2; 10 | bb0: { 11 | StorageLive(_1); 12 | _1 = 0_i32; 13 | StorageLive(_2); 14 | StorageLive(_3); 15 | _3 = &_1; 16 | _2 = {coroutine-closure@$DIR/async-closure.rs:9:13: 9:21}(move _3); 17 | StorageDead(_3); 18 | _0 = (); 19 | StorageDead(_2); 20 | StorageDead(_1); 21 | return; 22 | } 23 | } 24 | fn foo::{closure#0}(_1: &{async closure@$DIR/async-closure.rs:9:13: 9:21}) -> {async closure body@$DIR/async-closure.rs:9:22: 11:6} { 25 | let mut _0: {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 26 | let mut _2: &i32; 27 | let mut _3: &i32; 28 | debug y => (*((*_1).0: &i32)); 29 | bb0: { 30 | StorageLive(_2); 31 | _3 = CopyForDeref(((*_1).0: &i32)); 32 | _2 = &(*_3); 33 | _0 = {coroutine@$DIR/async-closure.rs:9:22: 11:6}(move _2); 34 | StorageDead(_2); 35 | return; 36 | } 37 | } 38 | fn foo::{closure#0}::{closure#0}(_1: std::pin::Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut std::task::Context<'_>) -> std::task::Poll<()> { 39 | let mut _0: std::task::Poll<()>; 40 | let _3: i32; 41 | let mut _4: &i32; 42 | let mut _5: (); 43 | let mut _6: &mut std::task::Context<'_>; 44 | let mut _7: u32; 45 | let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 46 | let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 47 | let mut _10: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 48 | debug _task_context => _6; 49 | debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); 50 | debug y => _3; 51 | bb0: { 52 | _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); 53 | _7 = discriminant((*_8)); 54 | switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb3]; 55 | } 56 | bb1: { 57 | _6 = move _2; 58 | StorageLive(_3); 59 | _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); 60 | _4 = CopyForDeref(((*_9).0: &i32)); 61 | _3 = (*_4); 62 | _5 = (); 63 | StorageDead(_3); 64 | _0 = std::task::Poll::Ready(move _5); 65 | _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); 66 | discriminant((*_10)) = 1; 67 | return; 68 | } 69 | bb2: { 70 | assert(false, `async fn` resumed after completion) -> [success: bb2, unwind unreachable]; 71 | } 72 | bb3: { 73 | unreachable; 74 | } 75 | } 76 | fn foo::{closure#0}::{synthetic#0}(_1: std::pin::Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut std::task::Context<'_>) -> std::task::Poll<()> { 77 | let mut _0: std::task::Poll<()>; 78 | let _3: i32; 79 | let mut _4: &i32; 80 | let mut _5: (); 81 | let mut _6: &mut std::task::Context<'_>; 82 | let mut _7: u32; 83 | let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 84 | let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 85 | let mut _10: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; 86 | debug _task_context => _6; 87 | debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); 88 | debug y => _3; 89 | bb0: { 90 | _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); 91 | _7 = discriminant((*_8)); 92 | switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb3]; 93 | } 94 | bb1: { 95 | _6 = move _2; 96 | StorageLive(_3); 97 | _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); 98 | _4 = CopyForDeref(((*_9).0: &i32)); 99 | _3 = (*_4); 100 | _5 = (); 101 | StorageDead(_3); 102 | _0 = std::task::Poll::Ready(move _5); 103 | _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); 104 | discriminant((*_10)) = 1; 105 | return; 106 | } 107 | bb2: { 108 | assert(false, `async fn` resumed after completion) -> [success: bb2, unwind unreachable]; 109 | } 110 | bb3: { 111 | unreachable; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /rustc_public/src/unstable/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module that collects the things that have no stability guarantees. 2 | //! 3 | //! We want to keep rustc_public's IR definitions and logic separate from 4 | //! any sort of conversion and usage of internal rustc code. So we 5 | //! restrict the usage of internal items to be inside this module. 6 | 7 | use std::marker::PointeeSized; 8 | 9 | use rustc_hir::def::DefKind; 10 | use rustc_middle::ty::{List, Ty, TyCtxt}; 11 | use rustc_middle::{mir, ty}; 12 | use rustc_public_bridge::Tables; 13 | use rustc_public_bridge::context::CompilerCtxt; 14 | 15 | use super::compiler_interface::BridgeTys; 16 | use crate::{CtorKind, ItemKind}; 17 | 18 | pub(crate) mod convert; 19 | mod internal_cx; 20 | 21 | /// Trait that defines the methods that are fine to call from [`RustcInternal`]. 22 | /// 23 | /// This trait is only for [`RustcInternal`]. Any other other access to rustc's internals 24 | /// should go through [`rustc_public_bridge::context::CompilerCtxt`]. 25 | pub trait InternalCx<'tcx>: Copy + Clone { 26 | fn tcx(self) -> TyCtxt<'tcx>; 27 | 28 | fn lift>>(self, value: T) -> Option; 29 | 30 | fn mk_args_from_iter(self, iter: I) -> T::Output 31 | where 32 | I: Iterator, 33 | T: ty::CollectAndApply, ty::GenericArgsRef<'tcx>>; 34 | 35 | fn mk_pat(self, v: ty::PatternKind<'tcx>) -> ty::Pattern<'tcx>; 36 | 37 | fn mk_poly_existential_predicates( 38 | self, 39 | eps: &[ty::PolyExistentialPredicate<'tcx>], 40 | ) -> &'tcx List>; 41 | 42 | fn mk_type_list(self, v: &[Ty<'tcx>]) -> &'tcx List>; 43 | 44 | fn lifetimes_re_erased(self) -> ty::Region<'tcx>; 45 | 46 | fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output 47 | where 48 | I: Iterator, 49 | T: ty::CollectAndApply>; 50 | 51 | fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List>; 52 | 53 | fn adt_def(self, def_id: rustc_hir::def_id::DefId) -> ty::AdtDef<'tcx>; 54 | } 55 | 56 | /// Trait used to convert between an internal MIR type to a rustc_public's IR type. 57 | /// 58 | /// This trait is currently exposed to users so they can have interoperability 59 | /// between internal MIR and rustc_public's IR constructs. 60 | /// However, they should be used seldom and they have no influence in this crate semver. 61 | #[doc(hidden)] 62 | pub trait Stable<'tcx>: PointeeSized { 63 | /// The stable representation of the type implementing Stable. 64 | type T; 65 | /// Converts an object to the equivalent rustc_public's IR representation. 66 | fn stable<'cx>( 67 | &self, 68 | tables: &mut Tables<'cx, BridgeTys>, 69 | cx: &CompilerCtxt<'cx, BridgeTys>, 70 | ) -> Self::T; 71 | } 72 | 73 | /// Trait used to translate a rustc_public's IR construct to its rustc counterpart. 74 | /// 75 | /// This is basically a mirror of [Stable]. 76 | /// 77 | /// This trait is currently exposed to users so they can have interoperability 78 | /// between internal MIR and rustc_public's IR constructs. 79 | /// They should be used seldom as they have no stability guarantees. 80 | #[doc(hidden)] 81 | pub trait RustcInternal { 82 | type T<'tcx>; 83 | fn internal<'tcx>( 84 | &self, 85 | tables: &mut Tables<'_, BridgeTys>, 86 | tcx: impl InternalCx<'tcx>, 87 | ) -> Self::T<'tcx>; 88 | } 89 | 90 | pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind { 91 | match kind { 92 | DefKind::Mod 93 | | DefKind::Struct 94 | | DefKind::Union 95 | | DefKind::Enum 96 | | DefKind::Variant 97 | | DefKind::Trait 98 | | DefKind::TyAlias 99 | | DefKind::ForeignTy 100 | | DefKind::TraitAlias 101 | | DefKind::AssocTy 102 | | DefKind::TyParam 103 | | DefKind::ConstParam 104 | | DefKind::Macro(_) 105 | | DefKind::ExternCrate 106 | | DefKind::Use 107 | | DefKind::ForeignMod 108 | | DefKind::OpaqueTy 109 | | DefKind::Field 110 | | DefKind::LifetimeParam 111 | | DefKind::Impl { .. } 112 | | DefKind::GlobalAsm => { 113 | unreachable!("Not a valid item kind: {kind:?}"); 114 | } 115 | DefKind::Closure | DefKind::AssocFn | DefKind::Fn | DefKind::SyntheticCoroutineBody => { 116 | ItemKind::Fn 117 | } 118 | DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => { 119 | ItemKind::Const 120 | } 121 | DefKind::Static { .. } => ItemKind::Static, 122 | DefKind::Ctor(_, rustc_hir::def::CtorKind::Const) => ItemKind::Ctor(CtorKind::Const), 123 | DefKind::Ctor(_, rustc_hir::def::CtorKind::Fn) => ItemKind::Ctor(CtorKind::Fn), 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /book/src/contributing.md: -------------------------------------------------------------------------------- 1 | # Developer documentation 2 | 3 | This folder contains some documentation useful for the development of Stable MIR 4 | 5 | ## Tools 6 | 7 | We currently have two different tools that is used to help with testing StableMIR 8 | 9 | ### TestDrive 10 | 11 | This is a small driver that we build on the top of the rust compiler. 12 | By default, this driver behaves exactly like `rustc`, and it can be used to build a crate 13 | or multiple packages using cargo. 14 | 15 | This driver also contains multiple checks that will inspect the stable MIR of the crate that was compiled. 16 | In order to run those tests, you need to pass an extra argument `--smir-check`. 17 | 18 | Let's say we have a crate `input.rs`, in order to run all checks, one should invoke: 19 | 20 | ```shell 21 | cargo run -p test-drive -- --smir-check test.rs 22 | # or run the test-drive directly where ${BIN_PATH} is the binary location 23 | ${BIN_PATH}/test-drive --smir-check test.rs 24 | ``` 25 | 26 | In order to run this as part of a cargo build, you can run from a workspace: 27 | 28 | ```shell 29 | # Only check SMIR for the target crate 30 | RUSTC=${BIN_PATH} cargo rustc -p ${TARGET_PACKAGE} -- --smir-check 31 | 32 | # Check SMIR for all crates being compiled, including dependencies 33 | RUSTC=${BIN_PATH} RUSTFLAGS=--smir-check cargo build 34 | ``` 35 | 36 | This driver accepts a few other options that are all preceded with `--smir` prefix[^outdated]. For example 37 | - `--smir-fixme`: Will run checks that currently trigger a known bug in StableMIR. 38 | - `--smir-verbose`: Print status and failure messages. 39 | 40 | [^outdated]: Since these are test tools, this documentation may be outdated. 41 | 42 | ### Compiletest 43 | 44 | This is a little utility built on the top of `ui_test` crate. 45 | It scans our test folders and run tests according to the selected mode. 46 | For more details on how to run this utility, check its help option: 47 | ```shell 48 | cargo run -p compiletest -- --help 49 | ``` 50 | 51 | ## Test Suites 52 | 53 | We have a few different test suites for Stable MIR: 54 | - **Rust compiler [`ui-fulldeps/rustc_public`](https://github.com/rust-lang/rust/tree/master/tests/ui-fulldeps/rustc_public):** 55 | These are very simple sanity checks that live inside the main rust repository. 56 | These tests should cover very basic functionality related to the translation of internal structures to stable ones. 57 | - **Rust compiler suites:** We are enabling the execution of rust compiler test suites. 58 | These suites are run with the rust respository compiletest. 59 | To run them, I recommend using our script `scripts/run_rustc_tests.sh` since there are a lot of arguments. 60 | - **Local suites:** These are suites hosted in this repository inside `tests/`. 61 | These are run using our local `compilest` and 62 | `scripts/run_own_tests.sh` is a script to run all local suites 63 | - `fixme`: Single file crates that are currently failing. 64 | These tests are run using `--smir-fixme`. 65 | - `sanity-checks`: Small examples that exercise some specific construct. 66 | These tests succeed if compilation and all smir checks succeed. 67 | 68 | ## Toolchain versions 69 | 70 | Our CI currently run a nightly job that build and run our test suites against the latest nightly toolchain. 71 | This repository also contains a .toolchain file that specifies to use `nightly` channel. 72 | 73 | However, if you already have the nightly toolchain installed, this will not update the toolchain to 74 | the latest nightly. 75 | You need to explicitly do that. 76 | 77 | If you see some errors while compiling our tools, please ensure you have the latest nightly. 78 | You might also want to check [our nightly runs](https://github.com/rust-lang/project-stable-mir/actions/workflows/nightly.yml?query=branch%2Amain) to ensure they are not currently broken. 79 | If so, you can check what was the last successful nightly run, and use its nightly version. 80 | 81 | ### Custom toolchain 82 | 83 | In order to run the tools and test suites using a local copy of `rustc`, do the following: 84 | 1. Build stage 2 of the compiler. 85 | See [`rustc` build guide](https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html) for more details. E.g.: 86 | ```shell 87 | git clone https://github.com/rust-lang/rust.git 88 | cd rust 89 | git checkout ${COMMIT_ID:?"Missing rustc commit id"} 90 | ./configure --enable-extended --tools=src,rustfmt,cargo --enable-debug --set=llvm.download-ci-llvm=true 91 | ./x.py build -i --stage 2 92 | ``` 93 | 94 | 2. Create a custom toolchain: 95 | ```shell 96 | # This will create a toolchain named "local" 97 | # Set the TARGET variable, e.g.: x86_64-unknown-linux-gnu 98 | rustup toolchain link local build/${TARGET}/stage2 99 | cp build/${TARGET}/stage2-tools-bin/* build/${TARGET}/stage2/bin/ 100 | ``` 101 | 3. Override the current toolchain in your `project-stable-mir` repository. 102 | ```shell 103 | cd ${SMIR_FOLDER} 104 | rustup override set local 105 | cargo clean 106 | cargo build 107 | ``` 108 | 109 | By default, the build scripts will use the active toolchain for the project folder. 110 | If you run step 3, the scripts should already pick up the local toolchain. 111 | Additionally, you can also set the rust toolchain by setting the TOOLCHAIN environment variable. 112 | E.g.: 113 | ```shell 114 | # Clean old build 115 | cargo clean 116 | 117 | # Run test with local toolchain 118 | TOOLCHAIN=local ./.github/scripts/run_own_tests.sh 119 | ``` 120 | 121 | Note: To remove the override created on step 3, run `rustup override unset` in the same folder. 122 | -------------------------------------------------------------------------------- /rustc_public/src/crate_def.rs: -------------------------------------------------------------------------------- 1 | //! Module that define a common trait for things that represent a crate definition, 2 | //! such as, a function, a trait, an enum, and any other definitions. 3 | 4 | use serde::Serialize; 5 | 6 | use crate::ty::{GenericArgs, Span, Ty}; 7 | use crate::{AssocItems, Crate, Symbol, with}; 8 | 9 | /// A unique identification number for each item accessible for the current compilation unit. 10 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] 11 | pub struct DefId(pub(crate) usize); 12 | 13 | impl DefId { 14 | /// Return fully qualified name of this definition 15 | pub fn name(&self) -> Symbol { 16 | with(|cx| cx.def_name(*self, false)) 17 | } 18 | 19 | /// Return a trimmed name of this definition. 20 | /// 21 | /// This can be used to print more user friendly diagnostic messages. 22 | /// 23 | /// If a symbol name can only be imported from one place for a type, and as 24 | /// long as it was not glob-imported anywhere in the current crate, we trim its 25 | /// path and print only the name. 26 | /// 27 | /// For example, this function may shorten `std::vec::Vec` to just `Vec`, 28 | /// as long as there is no other `Vec` importable anywhere. 29 | pub fn trimmed_name(&self) -> Symbol { 30 | with(|cx| cx.def_name(*self, true)) 31 | } 32 | } 33 | 34 | /// A trait for retrieving information about a particular definition. 35 | /// 36 | /// Implementors must provide the implementation of `def_id` which will be used to retrieve 37 | /// information about a crate's definition. 38 | pub trait CrateDef { 39 | /// Retrieve the unique identifier for the current definition. 40 | fn def_id(&self) -> DefId; 41 | 42 | /// Return the fully qualified name of the current definition. 43 | /// 44 | /// See [`DefId::name`] for more details 45 | fn name(&self) -> Symbol { 46 | self.def_id().name() 47 | } 48 | 49 | /// Return a trimmed name of this definition. 50 | /// 51 | /// See [`DefId::trimmed_name`] for more details 52 | fn trimmed_name(&self) -> Symbol { 53 | self.def_id().trimmed_name() 54 | } 55 | 56 | /// Return information about the crate where this definition is declared. 57 | /// 58 | /// This will return the crate number and its name. 59 | fn krate(&self) -> Crate { 60 | let def_id = self.def_id(); 61 | with(|cx| cx.krate(def_id)) 62 | } 63 | 64 | /// Return the span of this definition. 65 | fn span(&self) -> Span { 66 | let def_id = self.def_id(); 67 | with(|cx| cx.span_of_an_item(def_id)) 68 | } 69 | 70 | /// Return registered tool attributes with the given attribute name. 71 | /// 72 | /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool 73 | /// attributes will simply return an empty list. 74 | /// 75 | /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. 76 | /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. 77 | fn tool_attrs(&self, attr: &[Symbol]) -> Vec { 78 | let def_id = self.def_id(); 79 | with(|cx| cx.tool_attrs(def_id, attr)) 80 | } 81 | 82 | /// Return all tool attributes of this definition. 83 | fn all_tool_attrs(&self) -> Vec { 84 | let def_id = self.def_id(); 85 | with(|cx| cx.all_tool_attrs(def_id)) 86 | } 87 | } 88 | 89 | /// A trait that can be used to retrieve a definition's type. 90 | /// 91 | /// Note that not every CrateDef has a type `Ty`. They should not implement this trait. 92 | pub trait CrateDefType: CrateDef { 93 | /// Returns the type of this crate item. 94 | fn ty(&self) -> Ty { 95 | with(|cx| cx.def_ty(self.def_id())) 96 | } 97 | 98 | /// Retrieve the type of this definition by instantiating and normalizing it with `args`. 99 | /// 100 | /// This will panic if instantiation fails. 101 | fn ty_with_args(&self, args: &GenericArgs) -> Ty { 102 | with(|cx| cx.def_ty_with_args(self.def_id(), args)) 103 | } 104 | } 105 | 106 | /// A trait for retrieving all items from a definition within a crate. 107 | pub trait CrateDefItems: CrateDef { 108 | /// Retrieve all associated items from a definition. 109 | fn associated_items(&self) -> AssocItems { 110 | with(|cx| cx.associated_items(self.def_id())) 111 | } 112 | } 113 | 114 | #[derive(Clone, Debug, PartialEq, Eq)] 115 | pub struct Attribute { 116 | value: String, 117 | span: Span, 118 | } 119 | 120 | impl Attribute { 121 | pub fn new(value: String, span: Span) -> Attribute { 122 | Attribute { value, span } 123 | } 124 | 125 | /// Get the span of this attribute. 126 | pub fn span(&self) -> Span { 127 | self.span 128 | } 129 | 130 | /// Get the string representation of this attribute. 131 | pub fn as_str(&self) -> &str { 132 | &self.value 133 | } 134 | } 135 | 136 | macro_rules! crate_def { 137 | ( $(#[$attr:meta])* 138 | $vis:vis $name:ident $(;)? 139 | ) => { 140 | $(#[$attr])* 141 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 142 | $vis struct $name(pub DefId); 143 | 144 | impl CrateDef for $name { 145 | fn def_id(&self) -> DefId { 146 | self.0 147 | } 148 | } 149 | }; 150 | } 151 | 152 | macro_rules! crate_def_with_ty { 153 | ( $(#[$attr:meta])* 154 | $vis:vis $name:ident $(;)? 155 | ) => { 156 | $(#[$attr])* 157 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 158 | $vis struct $name(pub DefId); 159 | 160 | impl CrateDef for $name { 161 | fn def_id(&self) -> DefId { 162 | self.0 163 | } 164 | } 165 | 166 | impl CrateDefType for $name {} 167 | }; 168 | } 169 | 170 | macro_rules! impl_crate_def_items { 171 | ( $name:ident $(;)? ) => { 172 | impl CrateDefItems for $name {} 173 | }; 174 | } 175 | -------------------------------------------------------------------------------- /test-drive/src/sanity_checks.rs: -------------------------------------------------------------------------------- 1 | //! Module that contains sanity checks that Stable MIR APIs don't crash and that 2 | //! their result is coherent. 3 | //! 4 | //! These checks should only depend on StableMIR APIs. See other modules for tests that compare 5 | //! the result between StableMIR and internal APIs. 6 | use std::collections::HashSet; 7 | use std::fmt::Debug; 8 | use std::iter::zip; 9 | 10 | use rustc_public::mir::MirVisitor; 11 | use rustc_public::ty::{ImplDef, TraitDef}; 12 | use rustc_public::{self, CrateDef, mir, ty}; 13 | 14 | use crate::TestResult; 15 | 16 | fn check_equal(val: T, expected: T, msg: &str) -> TestResult 17 | where 18 | T: Debug + PartialEq, 19 | { 20 | if val != expected { 21 | Err(format!("{}: \n Expected: `{:?}`\n Found: `{:?}`", msg, expected, val)) 22 | } else { 23 | Ok(()) 24 | } 25 | } 26 | 27 | pub fn check(val: bool, msg: String) -> TestResult { 28 | if !val { Err(msg) } else { Ok(()) } 29 | } 30 | 31 | // Test that if there is an entry point, the function is part of `all_local_items`. 32 | pub fn test_entry_fn() -> TestResult { 33 | let entry_fn = rustc_public::entry_fn(); 34 | entry_fn.map_or(Ok(()), |entry_fn| { 35 | check_body(&entry_fn.name(), &entry_fn.body().unwrap())?; 36 | let all_items = rustc_public::all_local_items(); 37 | check(all_items.contains(&entry_fn), format!("Failed to find entry_fn: `{:?}`", entry_fn))?; 38 | check_equal(entry_fn.kind(), rustc_public::ItemKind::Fn, "Entry must be a function") 39 | }) 40 | } 41 | 42 | /// Iterate over local function bodies. 43 | pub fn test_all_fns() -> TestResult { 44 | let all_items = rustc_public::all_local_items(); 45 | for item in all_items { 46 | // Get body and iterate over items 47 | let body = item.body(); 48 | check_body(&item.name(), &body.unwrap())?; 49 | } 50 | Ok(()) 51 | } 52 | 53 | /// Test that we can retrieve information about the trait declaration for every trait implementation. 54 | pub fn test_traits() -> TestResult { 55 | let all_traits = HashSet::::from_iter(rustc_public::all_trait_decls().into_iter()); 56 | for trait_impl in rustc_public::all_trait_impls().iter().map(ImplDef::trait_impl) { 57 | check( 58 | all_traits.contains(&trait_impl.value.def_id), 59 | format!("Failed to find trait definition: `{trait_impl:?}`"), 60 | )?; 61 | } 62 | Ok(()) 63 | } 64 | 65 | pub fn test_crates() -> TestResult { 66 | for krate in rustc_public::external_crates() { 67 | check( 68 | rustc_public::find_crates(&krate.name.as_str()).contains(&krate), 69 | format!("Cannot find `{krate:?}`"), 70 | )?; 71 | } 72 | 73 | let local = rustc_public::local_crate(); 74 | check( 75 | rustc_public::find_crates(&local.name.as_str()).contains(&local), 76 | format!("Cannot find local: `{local:?}`"), 77 | ) 78 | } 79 | 80 | pub fn test_instances() -> TestResult { 81 | let all_items = rustc_public::all_local_items(); 82 | let mut queue = all_items 83 | .iter() 84 | .filter_map(|item| { 85 | (item.kind() == rustc_public::ItemKind::Fn) 86 | .then(|| mir::mono::Instance::try_from(*item).ok()) 87 | .flatten() 88 | }) 89 | .collect::>(); 90 | 91 | let mut visited = HashSet::::default(); 92 | while let Some(next_item) = queue.pop() { 93 | if visited.insert(next_item.clone()) { 94 | let Some(body) = next_item.body() else { 95 | continue; 96 | }; 97 | let visitor = check_body(&next_item.mangled_name(), &body)?; 98 | for term in visitor.terminators { 99 | match &term.kind { 100 | // We currently don't support Copy / Move `ty()` due to missing Place::ty(). 101 | // https://github.com/rust-lang/project-stable-mir/issues/49 102 | mir::TerminatorKind::Call { 103 | func: mir::Operand::Constant(constant), .. 104 | } => { 105 | match constant.ty().kind().rigid() { 106 | Some(ty::RigidTy::FnDef(def, args)) => { 107 | queue.push(mir::mono::Instance::resolve(*def, &args).unwrap()); 108 | } 109 | Some(ty::RigidTy::FnPtr(..)) => { /* ignore FnPtr for now */ } 110 | ty => check(false, format!("Unexpected call: `{ty:?}`"))?, 111 | } 112 | } 113 | _ => { /* Do nothing */ } 114 | } 115 | } 116 | } 117 | } 118 | Ok(()) 119 | } 120 | 121 | /// Visit all local types, statements and terminator to ensure nothing crashes. 122 | fn check_body(name: &str, body: &mir::Body) -> Result { 123 | let mut visitor = BodyVisitor::default(); 124 | visitor.visit_body(body); 125 | 126 | check_equal( 127 | body.blocks.len(), 128 | visitor.statements.len(), 129 | &format!("Function `{name}`: Unexpected visited BB statements"), 130 | )?; 131 | check_equal( 132 | body.blocks.len(), 133 | visitor.terminators.len(), 134 | &format!("Function `{name}`: Visited terminals"), 135 | )?; 136 | for (idx, bb) in body.blocks.iter().enumerate() { 137 | for (stmt, visited_stmt) in zip(&bb.statements, &visitor.statements[idx]) { 138 | check_equal(stmt, visited_stmt, &format!("Function `{name}`: Visited statement"))?; 139 | } 140 | check_equal( 141 | &bb.terminator, 142 | &visitor.terminators[idx], 143 | &format!("Function `{name}`: Terminator"), 144 | )?; 145 | } 146 | 147 | for local in body.locals() { 148 | if !visitor.types.contains(&local.ty) { 149 | // Format fails due to unsupported CoroutineWitness. 150 | // See https://github.com/rust-lang/project-stable-mir/issues/50. 151 | check(false, format!("Function `{name}`: Missing type `{:?}`", local.ty))?; 152 | }; 153 | } 154 | Ok(visitor) 155 | } 156 | 157 | #[derive(Debug, Default)] 158 | struct BodyVisitor { 159 | statements: Vec>, 160 | terminators: Vec, 161 | types: HashSet, 162 | } 163 | 164 | impl mir::MirVisitor for BodyVisitor { 165 | fn visit_basic_block(&mut self, bb: &mir::BasicBlock) { 166 | assert_eq!(self.statements.len(), self.terminators.len()); 167 | self.statements.push(vec![]); 168 | self.super_basic_block(bb) 169 | } 170 | fn visit_statement(&mut self, stmt: &mir::Statement, loc: mir::visit::Location) { 171 | self.statements.last_mut().unwrap().push(stmt.clone()); 172 | self.super_statement(stmt, loc) 173 | } 174 | 175 | fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) { 176 | self.terminators.push(term.clone()); 177 | self.super_terminator(term, location); 178 | } 179 | 180 | fn visit_ty(&mut self, ty: &ty::Ty, _location: mir::visit::Location) { 181 | self.types.insert(*ty); 182 | self.super_ty(ty) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /rustc_public/tests/compiletest.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::ffi::OsString; 3 | use std::path::PathBuf; 4 | use std::sync::OnceLock; 5 | 6 | use regex::bytes::Regex; 7 | use ui_test::color_eyre::eyre::Result; 8 | use ui_test::spanned::Spanned; 9 | use ui_test::status_emitter::StatusEmitter; 10 | use ui_test::{ 11 | Args, CommandBuilder, Config, Match, bless_output_files, error_on_output_conflict, 12 | run_tests_generic, 13 | }; 14 | 15 | #[derive(Copy, Clone, Debug)] 16 | /// Decides what is expected of each test's exit status. 17 | enum Mode { 18 | /// The test passes a full execution of the rustc driver. 19 | Pass, 20 | /// The rustc driver should emit an error. 21 | Fail, 22 | /// The test is currently failing but is expected to succeed. 23 | /// This is used to add test cases that reproduce an existing bug. This help us identify issues 24 | /// that may be "accidentally" fixed. 25 | FixMe, 26 | /// The test passes a full execution of `cargo build` 27 | CargoPass, 28 | } 29 | 30 | #[derive(Debug)] 31 | struct TestCx { 32 | /// Path for the rustc_public driver. 33 | driver_path: PathBuf, 34 | /// Arguments from the `cargo test` command. 35 | args: Args, 36 | } 37 | 38 | impl TestCx { 39 | fn new() -> Result { 40 | let driver_path: PathBuf = PathBuf::from( 41 | env::var("RP_TEST_DRIVER_PATH") 42 | .expect("RP_TEST_DRIVER_PATH must be set to run rustc_public test suites"), 43 | ); 44 | let args = Args::test()?; 45 | Ok(Self { driver_path, args }) 46 | } 47 | 48 | fn run_test(&mut self, mode: Mode, test_dir: &str) -> Result<()> { 49 | let config = config(mode, test_dir, self); 50 | eprintln!("Compiler: {}", config.program.display()); 51 | run_tests_generic( 52 | vec![config], 53 | ui_test::default_file_filter, 54 | ui_test::default_per_file_config, 55 | Box::::from(self.args.format), 56 | )?; 57 | 58 | Ok(()) 59 | } 60 | } 61 | 62 | fn config(mode: Mode, test_dir: &str, cx: &mut TestCx) -> Config { 63 | let config = if matches!(mode, Mode::CargoPass) { 64 | cargo_config(mode, test_dir, cx) 65 | } else { 66 | driver_config(mode, test_dir, cx) 67 | }; 68 | cx.args.bless |= env::var("RP_TEST_DRIVER_BLESS").is_ok_and(|v| v != "0"); 69 | 70 | common_settings(config, mode, cx.args.bless) 71 | } 72 | 73 | /// Configure cargo tests that will run the test-driver instead of rustc. 74 | fn cargo_config(mode: Mode, test_dir: &str, cx: &TestCx) -> Config { 75 | let mut config = Config::cargo(test_dir); 76 | config 77 | .program 78 | .envs 79 | .push(("RUST".into(), Some(cx.driver_path.clone().into_os_string()))); 80 | config.program.envs.push(( 81 | "CARGO_ENCODED_RUSTFLAGS".into(), 82 | Some(rustc_flags(mode).join(&OsString::from("\x1f")).into()), 83 | )); 84 | config 85 | } 86 | 87 | /// Configure tests that will invoke the test-driver directly as rustc. 88 | fn driver_config(mode: Mode, test_dir: &str, cx: &TestCx) -> Config { 89 | let mut config = Config::rustc(test_dir); 90 | config.program = CommandBuilder::rustc(); 91 | config.program.program = cx.driver_path.clone(); 92 | config.program.args = rustc_flags(mode); 93 | config 94 | } 95 | 96 | fn common_settings(mut config: Config, mode: Mode, bless: bool) -> Config { 97 | // Recommend that users should use this command to bless failing tests. 98 | config.bless_command = Some("./x test --bless".into()); 99 | config.output_conflict_handling = if bless { 100 | bless_output_files 101 | } else { 102 | error_on_output_conflict 103 | }; 104 | config.comment_defaults.base().exit_status = match mode { 105 | Mode::Pass | Mode::CargoPass => Some(0), 106 | Mode::Fail | Mode::FixMe => Some(1), 107 | } 108 | .map(Spanned::dummy) 109 | .into(); 110 | config.comment_defaults.base().require_annotations = 111 | Spanned::dummy(matches!(mode, Mode::Fail)).into(); 112 | config.comment_defaults.base().normalize_stderr = stderr_filters() 113 | .iter() 114 | .map(|(m, p)| (m.clone(), p.to_vec())) 115 | .collect(); 116 | config.comment_defaults.base().normalize_stdout = stdout_filters() 117 | .iter() 118 | .map(|(m, p)| (m.clone(), p.to_vec())) 119 | .collect(); 120 | config.out_dir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("rp_tests"); 121 | config 122 | } 123 | 124 | fn rustc_flags(mode: Mode) -> Vec { 125 | let mut flags = vec!["--smir-check".into()]; 126 | let verbose = env::var("RP_TEST_DRIVER_VERBOSE").is_ok_and(|v| v != "0"); 127 | if verbose { 128 | flags.push("--smir-verbose".into()); 129 | } 130 | if matches!(mode, Mode::FixMe) { 131 | // Enable checks that should pass but may trigger an existing issue. 132 | flags.push("--smir-fixme".into()); 133 | } 134 | flags 135 | } 136 | 137 | fn run_all(mut cx: TestCx) -> Result<()> { 138 | cx.run_test(Mode::Pass, "tests/sanity-checks")?; 139 | cx.run_test(Mode::Pass, "tests/print")?; 140 | cx.run_test(Mode::FixMe, "tests/fixme")?; 141 | cx.run_test(Mode::Fail, "tests/fail")?; 142 | Ok(()) 143 | } 144 | 145 | fn main() -> Result<()> { 146 | let cx = TestCx::new()?; 147 | run_all(cx)?; 148 | Ok(()) 149 | } 150 | 151 | macro_rules! regexes { 152 | ($name:ident: $($regex:expr => $replacement:expr,)*) => { 153 | fn $name() -> &'static [(Match, &'static [u8])] { 154 | static S: OnceLock> = OnceLock::new(); 155 | S.get_or_init(|| vec![ 156 | $((Regex::new($regex).unwrap().into(), $replacement.as_bytes()),)* 157 | ]) 158 | } 159 | }; 160 | } 161 | 162 | regexes! { 163 | stdout_filters: 164 | // Windows file paths 165 | r"\\" => "/", 166 | // erase borrow tags 167 | "<[0-9]+>" => "", 168 | "<[0-9]+=" => " "$$DIR", 171 | } 172 | 173 | regexes! { 174 | stderr_filters: 175 | // erase line and column info 176 | r"\.rs:[0-9]+:[0-9]+(: [0-9]+:[0-9]+)?" => ".rs:LL:CC", 177 | // erase alloc ids 178 | "alloc[0-9]+" => "ALLOC", 179 | // erase thread ids 180 | r"unnamed-[0-9]+" => "unnamed-ID", 181 | r"thread '(?P.*?)' \(\d+\) panicked" => "thread '$name' ($$TID) panicked", 182 | // erase borrow tags 183 | "<[0-9]+>" => "", 184 | "<[0-9]+=" => " "$1", 187 | // erase whitespace that differs between platforms 188 | r" +at (.*\.rs)" => " at $1", 189 | // erase generics in backtraces 190 | "([0-9]+: .*)::<.*>" => "$1", 191 | // erase long hexadecimals 192 | r"0x[0-9a-fA-F]+[0-9a-fA-F]{2,2}" => "$$HEX", 193 | // erase specific alignments 194 | "alignment [0-9]+" => "alignment ALIGN", 195 | "[0-9]+ byte alignment but found [0-9]+" => "ALIGN byte alignment but found ALIGN", 196 | // erase thread caller ids 197 | r"call [0-9]+" => "call ID", 198 | // erase platform module paths 199 | r"\bsys::([a-z_]+)::[a-z]+::" => "sys::$1::PLATFORM::", 200 | // Windows file paths 201 | r"\\" => "/", 202 | // test-drive tags 203 | r"\[T-DRIVE\].*\n" => "", 204 | // erase Rust stdlib path 205 | "[^ \n`]*/(rust[^/]*|checkout)/library/" => "RUSTLIB/", 206 | // erase rustc path 207 | "[^ \n`]*/(rust[^/]*|checkout)/compiler/" => "RUSTC/", 208 | // erase platform file paths 209 | r"\bsys/([a-z_]+)/[a-z]+\b" => "sys/$1/PLATFORM", 210 | // erase paths into the crate registry 211 | r"[^ ]*/\.?cargo/registry/.*/(.*\.rs)" => "CARGO_REGISTRY/.../$1", 212 | } 213 | -------------------------------------------------------------------------------- /rustc_public/src/visitor.rs: -------------------------------------------------------------------------------- 1 | use std::ops::ControlFlow; 2 | 3 | use super::ty::{ 4 | Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, 5 | MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst, 6 | }; 7 | use crate::Opaque; 8 | use crate::ty::TyConst; 9 | 10 | pub trait Visitor: Sized { 11 | type Break; 12 | fn visit_ty(&mut self, ty: &Ty) -> ControlFlow { 13 | ty.super_visit(self) 14 | } 15 | fn visit_const(&mut self, c: &TyConst) -> ControlFlow { 16 | c.super_visit(self) 17 | } 18 | fn visit_reg(&mut self, reg: &Region) -> ControlFlow { 19 | reg.super_visit(self) 20 | } 21 | } 22 | 23 | pub trait Visitable { 24 | fn visit(&self, visitor: &mut V) -> ControlFlow { 25 | self.super_visit(visitor) 26 | } 27 | fn super_visit(&self, visitor: &mut V) -> ControlFlow; 28 | } 29 | 30 | impl Visitable for Ty { 31 | fn visit(&self, visitor: &mut V) -> ControlFlow { 32 | visitor.visit_ty(self) 33 | } 34 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 35 | match self.kind() { 36 | super::ty::TyKind::RigidTy(ty) => ty.visit(visitor)?, 37 | super::ty::TyKind::Alias(_, alias) => alias.args.visit(visitor)?, 38 | super::ty::TyKind::Param(_) | super::ty::TyKind::Bound(_, _) => {} 39 | } 40 | ControlFlow::Continue(()) 41 | } 42 | } 43 | 44 | impl Visitable for TyConst { 45 | fn visit(&self, visitor: &mut V) -> ControlFlow { 46 | visitor.visit_const(self) 47 | } 48 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 49 | match &self.kind { 50 | super::ty::TyConstKind::Param(_) | super::ty::TyConstKind::Bound(_, _) => {} 51 | super::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?, 52 | super::ty::TyConstKind::Value(ty, alloc) => { 53 | alloc.visit(visitor)?; 54 | ty.visit(visitor)?; 55 | } 56 | super::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?, 57 | } 58 | ControlFlow::Continue(()) 59 | } 60 | } 61 | 62 | impl Visitable for MirConst { 63 | fn visit(&self, visitor: &mut V) -> ControlFlow { 64 | self.super_visit(visitor) 65 | } 66 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 67 | match &self.kind() { 68 | super::ty::ConstantKind::Ty(ct) => ct.visit(visitor)?, 69 | super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?, 70 | super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?, 71 | super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {} 72 | } 73 | self.ty().visit(visitor) 74 | } 75 | } 76 | 77 | impl Visitable for Opaque { 78 | fn super_visit(&self, _visitor: &mut V) -> ControlFlow { 79 | ControlFlow::Continue(()) 80 | } 81 | } 82 | 83 | impl Visitable for Allocation { 84 | fn super_visit(&self, _visitor: &mut V) -> ControlFlow { 85 | ControlFlow::Continue(()) 86 | } 87 | } 88 | 89 | impl Visitable for UnevaluatedConst { 90 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 91 | let UnevaluatedConst { def, args, promoted } = self; 92 | def.visit(visitor)?; 93 | args.visit(visitor)?; 94 | promoted.visit(visitor) 95 | } 96 | } 97 | 98 | impl Visitable for ConstDef { 99 | fn super_visit(&self, _visitor: &mut V) -> ControlFlow { 100 | ControlFlow::Continue(()) 101 | } 102 | } 103 | 104 | impl Visitable for Option { 105 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 106 | match self { 107 | Some(val) => val.visit(visitor), 108 | None => ControlFlow::Continue(()), 109 | } 110 | } 111 | } 112 | 113 | impl Visitable for Promoted { 114 | fn super_visit(&self, _visitor: &mut V) -> ControlFlow { 115 | ControlFlow::Continue(()) 116 | } 117 | } 118 | 119 | impl Visitable for GenericArgs { 120 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 121 | self.0.visit(visitor) 122 | } 123 | } 124 | 125 | impl Visitable for Region { 126 | fn visit(&self, visitor: &mut V) -> ControlFlow { 127 | visitor.visit_reg(self) 128 | } 129 | 130 | fn super_visit(&self, _: &mut V) -> ControlFlow { 131 | ControlFlow::Continue(()) 132 | } 133 | } 134 | 135 | impl Visitable for GenericArgKind { 136 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 137 | match self { 138 | GenericArgKind::Lifetime(lt) => lt.visit(visitor), 139 | GenericArgKind::Type(t) => t.visit(visitor), 140 | GenericArgKind::Const(c) => c.visit(visitor), 141 | } 142 | } 143 | } 144 | 145 | impl Visitable for RigidTy { 146 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 147 | match self { 148 | RigidTy::Bool 149 | | RigidTy::Char 150 | | RigidTy::Int(_) 151 | | RigidTy::Uint(_) 152 | | RigidTy::Float(_) 153 | | RigidTy::Never 154 | | RigidTy::Foreign(_) 155 | | RigidTy::Str => ControlFlow::Continue(()), 156 | RigidTy::Array(t, c) => { 157 | t.visit(visitor)?; 158 | c.visit(visitor) 159 | } 160 | RigidTy::Pat(t, _p) => t.visit(visitor), 161 | RigidTy::Slice(inner) => inner.visit(visitor), 162 | RigidTy::RawPtr(ty, _) => ty.visit(visitor), 163 | RigidTy::Ref(reg, ty, _) => { 164 | reg.visit(visitor)?; 165 | ty.visit(visitor) 166 | } 167 | RigidTy::Adt(_, args) 168 | | RigidTy::Closure(_, args) 169 | | RigidTy::Coroutine(_, args) 170 | | RigidTy::CoroutineWitness(_, args) 171 | | RigidTy::CoroutineClosure(_, args) 172 | | RigidTy::FnDef(_, args) => args.visit(visitor), 173 | RigidTy::FnPtr(sig) => sig.visit(visitor), 174 | RigidTy::Dynamic(pred, r, _) => { 175 | pred.visit(visitor)?; 176 | r.visit(visitor) 177 | } 178 | RigidTy::Tuple(fields) => fields.visit(visitor), 179 | } 180 | } 181 | } 182 | 183 | impl Visitable for Vec { 184 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 185 | for arg in self { 186 | arg.visit(visitor)?; 187 | } 188 | ControlFlow::Continue(()) 189 | } 190 | } 191 | 192 | impl Visitable for Binder { 193 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 194 | self.value.visit(visitor) 195 | } 196 | } 197 | 198 | impl Visitable for ExistentialPredicate { 199 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 200 | match self { 201 | ExistentialPredicate::Trait(tr) => tr.generic_args.visit(visitor), 202 | ExistentialPredicate::Projection(p) => { 203 | p.term.visit(visitor)?; 204 | p.generic_args.visit(visitor) 205 | } 206 | ExistentialPredicate::AutoTrait(_) => ControlFlow::Continue(()), 207 | } 208 | } 209 | } 210 | 211 | impl Visitable for TermKind { 212 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 213 | match self { 214 | TermKind::Type(t) => t.visit(visitor), 215 | TermKind::Const(c) => c.visit(visitor), 216 | } 217 | } 218 | } 219 | 220 | impl Visitable for FnSig { 221 | fn super_visit(&self, visitor: &mut V) -> ControlFlow { 222 | self.inputs_and_output.visit(visitor) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /devtool/src/main.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use std::env; 4 | use std::path::PathBuf; 5 | 6 | use anyhow::Result; 7 | use clap::{Parser, Subcommand}; 8 | use xshell::{Cmd, Shell, cmd}; 9 | 10 | use crate::utils::active_toolchain; 11 | 12 | #[derive(Debug, Subcommand)] 13 | pub enum Command { 14 | /// Build rustc_public itself. 15 | Build { 16 | /// Flags that are passed through to `cargo build`. 17 | #[arg(trailing_var_arg = true, allow_hyphen_values = true)] 18 | flags: Vec, 19 | }, 20 | /// Run rustc_public test suites. 21 | Test { 22 | /// Overwrite *.stderr/stdout files. 23 | #[arg(long)] 24 | bless: bool, 25 | /// Run test-drive on verbose mode to print test outputs. 26 | #[arg(long)] 27 | verbose: bool, 28 | /// Flags that are passed through to the test harness. 29 | #[arg(trailing_var_arg = true, allow_hyphen_values = true)] 30 | flags: Vec, 31 | }, 32 | /// Clean out build directories. 33 | Clean { 34 | /// Flags that are passed through to `cargo clean`. 35 | #[arg(trailing_var_arg = true, allow_hyphen_values = true)] 36 | flags: Vec, 37 | }, 38 | /// Run rustfmt. 39 | Fmt { 40 | /// Run rustfmt check. 41 | #[arg(long)] 42 | check: bool, 43 | /// Flags that are passed through to `rustfmt`. 44 | #[arg(trailing_var_arg = true, allow_hyphen_values = true)] 45 | flags: Vec, 46 | }, 47 | /// Bump the Minimum Supported Rust Version. 48 | MSRV { 49 | /// The nightly version you want to bump to. Note that it should be a date. 50 | #[arg(long)] 51 | date: String, 52 | }, 53 | /// Install the git hook to perform format check just before pushing. 54 | Githook { 55 | #[command(subcommand)] 56 | command: GithookCommand, 57 | }, 58 | } 59 | 60 | #[derive(Debug, Subcommand)] 61 | pub enum GithookCommand { 62 | /// Install our `pre-push.sh` to `.git/hooks/pre-push`. 63 | Install, 64 | /// Uninstall our `pre-push.sh` from `.git/hooks/pre-push`. 65 | Uninstall, 66 | } 67 | 68 | #[derive(Parser)] 69 | #[command(name = "devtool")] 70 | pub struct Cli { 71 | #[command(subcommand)] 72 | pub command: Command, 73 | } 74 | 75 | pub struct DevCx { 76 | /// The root path of the rustc_public checkout. 77 | root_dir: PathBuf, 78 | /// The toolchain that we use to compile our crates. 79 | toolchain: String, 80 | /// We use this shell to execute commands. 81 | sh: Shell, 82 | /// The cargo we use. 83 | cargo_bin: String, 84 | } 85 | 86 | impl DevCx { 87 | pub fn new() -> Result { 88 | let root_dir = utils::rustc_public_dir(); 89 | let toolchain = active_toolchain()?; 90 | let sh: Shell = Shell::new()?; 91 | let cargo_bin = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 92 | Ok(Self { root_dir, toolchain, sh, cargo_bin }) 93 | } 94 | 95 | pub fn cargo(&self, cmd: &str, crate_dir: &str) -> Cmd<'_> { 96 | let Self { root_dir, toolchain, sh, cargo_bin } = self; 97 | let mainfest: PathBuf = 98 | [root_dir.to_str().unwrap(), crate_dir, "Cargo.toml"].iter().collect(); 99 | cmd!(sh, "{cargo_bin} +{toolchain} {cmd} --manifest-path {mainfest}") 100 | } 101 | 102 | pub fn git(&self, cmd: &str) -> Cmd<'_> { 103 | cmd!(self.sh, "git {cmd}") 104 | } 105 | 106 | pub fn build(&self, crate_dir: &str, flags: &Vec) -> Result<()> { 107 | let cmd = self.cargo("build", crate_dir).args(&["--all-targets"]).args(flags); 108 | cmd.run()?; 109 | Ok(()) 110 | } 111 | 112 | pub fn test(&self, bless: bool, verbose: bool, flags: &Vec) -> Result<()> { 113 | let test_drive_path = 114 | utils::rustc_public_dir().join("target").join("debug").join("test-drive"); 115 | // compiletest needs this var to run test suites 116 | self.sh.set_var("RP_TEST_DRIVER_PATH", test_drive_path); 117 | if verbose { 118 | self.sh.set_var("RP_TEST_DRIVER_VERBOSE", "Ciallo"); 119 | } 120 | if bless { 121 | self.sh.set_var("RP_TEST_DRIVER_BLESS", "Ciallo"); 122 | } 123 | let cmd = self.cargo("test", "rustc_public").args(flags); 124 | cmd.run()?; 125 | Ok(()) 126 | } 127 | 128 | pub fn clean(&self, flags: &Vec) -> Result<()> { 129 | let cmd = self.cargo("clean", ".").args(flags); 130 | cmd.run()?; 131 | Ok(()) 132 | } 133 | 134 | pub fn fmt(&self, check: bool, crate_dir: &str, flags: &Vec) -> Result<()> { 135 | let mut cmd = self.cargo("fmt", crate_dir).args(flags); 136 | if check { 137 | cmd = cmd.args(&["--check"]); 138 | } 139 | cmd.run()?; 140 | Ok(()) 141 | } 142 | 143 | pub fn bump_msrv(&self, date: String) -> Result<()> { 144 | let _ = date; 145 | todo!() 146 | } 147 | 148 | pub fn install_git_book(&self) -> Result<()> { 149 | let cmd = self.git("rev-parse").arg("--git-common-dir"); 150 | let git_dir = cmd.read()?; 151 | let git_dir = PathBuf::from(git_dir.trim()); 152 | let hooks_dir = git_dir.join("hooks"); 153 | let dst = hooks_dir.join("pre-push"); 154 | if dst.exists() { 155 | // The git hook has already been set up. 156 | println!("`.git/hooks/pre-push` has already been installed."); 157 | return Ok(()); 158 | } 159 | if !hooks_dir.exists() { 160 | let _ = std::fs::create_dir(hooks_dir); 161 | } 162 | let pre_push = self.root_dir.join("scripts").join("pre-push.sh"); 163 | match std::fs::hard_link(pre_push, &dst) { 164 | Err(e) => { 165 | eprintln!( 166 | "ERROR: could not create hook {}: do you already have the git hook installed?\n{}", 167 | dst.display(), 168 | e 169 | ); 170 | return Err(e.into()); 171 | } 172 | Ok(_) => println!("Linked `scripts/pre-push.sh` to `.git/hooks/pre-push`"), 173 | }; 174 | Ok(()) 175 | } 176 | 177 | pub fn uninstall_git_book(&self) -> Result<()> { 178 | let cmd = self.git("rev-parse").arg("--git-common-dir"); 179 | let git_dir = cmd.read()?; 180 | let git_dir = PathBuf::from(git_dir.trim()); 181 | let hooks_dir = git_dir.join("hooks"); 182 | let dst = hooks_dir.join("pre-push"); 183 | if !dst.exists() { 184 | // The git hook has not been set up yet. 185 | println!("`.git/hooks/pre-push` has not been set up yet."); 186 | return Ok(()); 187 | } 188 | match std::fs::remove_file(&dst) { 189 | Ok(_) => println!("`.git/hooks/pre-push` is removed"), 190 | Err(e) => { 191 | eprintln!("ERROR: could not remove hook {}\n{}", dst.display(), e); 192 | return Err(e.into()); 193 | } 194 | } 195 | Ok(()) 196 | } 197 | } 198 | 199 | fn main() -> Result<()> { 200 | let cli = Cli::parse(); 201 | match cli.command { 202 | Command::Build { flags } => { 203 | let cx = DevCx::new()?; 204 | cx.build("rustc_public", &flags)?; 205 | Ok(()) 206 | } 207 | Command::Test { bless, verbose, flags } => { 208 | let cx = DevCx::new()?; 209 | cx.build("rustc_public", &flags)?; 210 | cx.build("test-drive", &flags)?; 211 | cx.test(bless, verbose, &flags)?; 212 | Ok(()) 213 | } 214 | Command::Clean { flags } => { 215 | let cx = DevCx::new()?; 216 | cx.clean(&flags)?; 217 | Ok(()) 218 | } 219 | Command::MSRV { date } => { 220 | let cx = DevCx::new()?; 221 | cx.bump_msrv(date)?; 222 | Ok(()) 223 | } 224 | Command::Fmt { check, flags } => { 225 | let cx = DevCx::new()?; 226 | cx.fmt(check, "rustc_public", &flags)?; 227 | cx.fmt(check, "devtool", &flags)?; 228 | cx.fmt(check, "test-drive", &flags)?; 229 | Ok(()) 230 | } 231 | Command::Githook { command } => { 232 | let cx = DevCx::new()?; 233 | match command { 234 | GithookCommand::Install => cx.install_git_book()?, 235 | GithookCommand::Uninstall => cx.uninstall_git_book()?, 236 | } 237 | Ok(()) 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /rustc_public/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The WIP public interface to rustc internals. 2 | //! 3 | //! For more information see 4 | //! 5 | //! # Note 6 | //! 7 | //! This API is still completely unstable and subject to change. 8 | 9 | #![allow(rustc::usage_of_ty_tykind)] 10 | #![cfg_attr(not(feature = "rustc-build"), feature(rustc_private))] 11 | #![doc( 12 | html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", 13 | test(attr(allow(unused_variables), deny(warnings))) 14 | )] 15 | #![feature(sized_hierarchy)] 16 | //! 17 | //! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to 18 | //! interact with the compiler. 19 | //! 20 | //! The goal is to eventually be published on 21 | //! [crates.io](https://crates.io). 22 | 23 | #[cfg(not(feature = "rustc-build"))] 24 | macro_rules! rustc_crates { 25 | () => { 26 | extern crate rustc_abi; 27 | extern crate rustc_driver; 28 | extern crate rustc_hir; 29 | extern crate rustc_middle; 30 | extern crate rustc_public_bridge; 31 | extern crate rustc_session; 32 | extern crate rustc_span; 33 | extern crate rustc_target; 34 | }; 35 | } 36 | 37 | #[cfg(not(feature = "rustc-build"))] 38 | rustc_crates!(); 39 | 40 | use std::fmt::Debug; 41 | use std::{fmt, io}; 42 | 43 | pub(crate) use rustc_public_bridge::IndexedVal; 44 | use rustc_public_bridge::Tables; 45 | use rustc_public_bridge::context::CompilerCtxt; 46 | /// Export the rustc_internal APIs. Note that this module has no stability 47 | /// guarantees and it is not taken into account for semver. 48 | #[cfg(feature = "rustc_internal")] 49 | pub mod rustc_internal; 50 | use serde::Serialize; 51 | 52 | use crate::compiler_interface::with; 53 | pub use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId}; 54 | pub use crate::error::*; 55 | use crate::mir::mono::StaticDef; 56 | use crate::mir::{Body, Mutability}; 57 | use crate::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, ProvenanceMap, Span, TraitDef, Ty}; 58 | use crate::unstable::Stable; 59 | 60 | pub mod abi; 61 | mod alloc; 62 | pub(crate) mod unstable; 63 | #[macro_use] 64 | pub mod crate_def; 65 | pub mod compiler_interface; 66 | #[macro_use] 67 | pub mod error; 68 | pub mod mir; 69 | pub mod target; 70 | pub mod ty; 71 | pub mod visitor; 72 | 73 | /// Use String for now but we should replace it. 74 | pub type Symbol = String; 75 | 76 | /// The number that identifies a crate. 77 | pub type CrateNum = usize; 78 | 79 | impl Debug for DefId { 80 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 81 | f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish() 82 | } 83 | } 84 | 85 | impl IndexedVal for DefId { 86 | fn to_val(index: usize) -> Self { 87 | DefId(index) 88 | } 89 | 90 | fn to_index(&self) -> usize { 91 | self.0 92 | } 93 | } 94 | 95 | /// A list of crate items. 96 | pub type CrateItems = Vec; 97 | 98 | /// A list of trait decls. 99 | pub type TraitDecls = Vec; 100 | 101 | /// A list of impl trait decls. 102 | pub type ImplTraitDecls = Vec; 103 | 104 | /// A list of associated items. 105 | pub type AssocItems = Vec; 106 | 107 | /// Holds information about a crate. 108 | #[derive(Clone, PartialEq, Eq, Debug, Serialize)] 109 | pub struct Crate { 110 | pub id: CrateNum, 111 | pub name: Symbol, 112 | pub is_local: bool, 113 | } 114 | 115 | impl Crate { 116 | /// The list of foreign modules in this crate. 117 | pub fn foreign_modules(&self) -> Vec { 118 | with(|cx| cx.foreign_modules(self.id)) 119 | } 120 | 121 | /// The list of traits declared in this crate. 122 | pub fn trait_decls(&self) -> TraitDecls { 123 | with(|cx| cx.trait_decls(self.id)) 124 | } 125 | 126 | /// The list of trait implementations in this crate. 127 | pub fn trait_impls(&self) -> ImplTraitDecls { 128 | with(|cx| cx.trait_impls(self.id)) 129 | } 130 | 131 | /// Return a list of function definitions from this crate independent on their visibility. 132 | pub fn fn_defs(&self) -> Vec { 133 | with(|cx| cx.crate_functions(self.id)) 134 | } 135 | 136 | /// Return a list of static items defined in this crate independent on their visibility. 137 | pub fn statics(&self) -> Vec { 138 | with(|cx| cx.crate_statics(self.id)) 139 | } 140 | } 141 | 142 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] 143 | pub enum ItemKind { 144 | Fn, 145 | Static, 146 | Const, 147 | Ctor(CtorKind), 148 | } 149 | 150 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] 151 | pub enum CtorKind { 152 | Const, 153 | Fn, 154 | } 155 | 156 | pub type Filename = String; 157 | 158 | crate_def_with_ty! { 159 | /// Holds information about an item in a crate. 160 | #[derive(Serialize)] 161 | pub CrateItem; 162 | } 163 | 164 | impl CrateItem { 165 | /// This will return the body of an item or panic if it's not available. 166 | pub fn expect_body(&self) -> mir::Body { 167 | with(|cx| cx.mir_body(self.0)) 168 | } 169 | 170 | /// Return the body of an item if available. 171 | pub fn body(&self) -> Option { 172 | with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0))) 173 | } 174 | 175 | /// Check if a body is available for this item. 176 | pub fn has_body(&self) -> bool { 177 | with(|cx| cx.has_body(self.0)) 178 | } 179 | 180 | pub fn span(&self) -> Span { 181 | with(|cx| cx.span_of_an_item(self.0)) 182 | } 183 | 184 | pub fn kind(&self) -> ItemKind { 185 | with(|cx| cx.item_kind(*self)) 186 | } 187 | 188 | pub fn requires_monomorphization(&self) -> bool { 189 | with(|cx| cx.requires_monomorphization(self.0)) 190 | } 191 | 192 | pub fn ty(&self) -> Ty { 193 | with(|cx| cx.def_ty(self.0)) 194 | } 195 | 196 | pub fn is_foreign_item(&self) -> bool { 197 | with(|cx| cx.is_foreign_item(self.0)) 198 | } 199 | 200 | /// Emit MIR for this item body. 201 | pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { 202 | self.body() 203 | .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))? 204 | .dump(w, &self.name()) 205 | } 206 | } 207 | 208 | /// Return the function where execution starts if the current 209 | /// crate defines that. This is usually `main`, but could be 210 | /// `start` if the crate is a no-std crate. 211 | pub fn entry_fn() -> Option { 212 | with(|cx| cx.entry_fn()) 213 | } 214 | 215 | /// Access to the local crate. 216 | pub fn local_crate() -> Crate { 217 | with(|cx| cx.local_crate()) 218 | } 219 | 220 | /// Try to find a crate or crates if multiple crates exist from given name. 221 | pub fn find_crates(name: &str) -> Vec { 222 | with(|cx| cx.find_crates(name)) 223 | } 224 | 225 | /// Try to find a crate with the given name. 226 | pub fn external_crates() -> Vec { 227 | with(|cx| cx.external_crates()) 228 | } 229 | 230 | /// Retrieve all items in the local crate that have a MIR associated with them. 231 | pub fn all_local_items() -> CrateItems { 232 | with(|cx| cx.all_local_items()) 233 | } 234 | 235 | pub fn all_trait_decls() -> TraitDecls { 236 | with(|cx| cx.all_trait_decls()) 237 | } 238 | 239 | pub fn all_trait_impls() -> ImplTraitDecls { 240 | with(|cx| cx.all_trait_impls()) 241 | } 242 | 243 | /// A type that provides internal information but that can still be used for debug purpose. 244 | #[derive(Clone, PartialEq, Eq, Hash, Serialize)] 245 | pub struct Opaque(String); 246 | 247 | impl std::fmt::Display for Opaque { 248 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 249 | write!(f, "{}", self.0) 250 | } 251 | } 252 | 253 | impl std::fmt::Debug for Opaque { 254 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 255 | write!(f, "{}", self.0) 256 | } 257 | } 258 | 259 | pub fn opaque(value: &T) -> Opaque { 260 | Opaque(format!("{value:?}")) 261 | } 262 | 263 | macro_rules! bridge_impl { 264 | ($name: ident, $ty: ty) => { 265 | impl rustc_public_bridge::bridge::$name for $ty { 266 | fn new(def: crate::DefId) -> Self { 267 | Self(def) 268 | } 269 | } 270 | }; 271 | } 272 | 273 | bridge_impl!(CrateItem, crate::CrateItem); 274 | bridge_impl!(AdtDef, crate::ty::AdtDef); 275 | bridge_impl!(ForeignModuleDef, crate::ty::ForeignModuleDef); 276 | bridge_impl!(ForeignDef, crate::ty::ForeignDef); 277 | bridge_impl!(FnDef, crate::ty::FnDef); 278 | bridge_impl!(ClosureDef, crate::ty::ClosureDef); 279 | bridge_impl!(CoroutineDef, crate::ty::CoroutineDef); 280 | bridge_impl!(CoroutineClosureDef, crate::ty::CoroutineClosureDef); 281 | bridge_impl!(AliasDef, crate::ty::AliasDef); 282 | bridge_impl!(ParamDef, crate::ty::ParamDef); 283 | bridge_impl!(BrNamedDef, crate::ty::BrNamedDef); 284 | bridge_impl!(TraitDef, crate::ty::TraitDef); 285 | bridge_impl!(GenericDef, crate::ty::GenericDef); 286 | bridge_impl!(ConstDef, crate::ty::ConstDef); 287 | bridge_impl!(ImplDef, crate::ty::ImplDef); 288 | bridge_impl!(RegionDef, crate::ty::RegionDef); 289 | bridge_impl!(CoroutineWitnessDef, crate::ty::CoroutineWitnessDef); 290 | bridge_impl!(AssocDef, crate::ty::AssocDef); 291 | bridge_impl!(OpaqueDef, crate::ty::OpaqueDef); 292 | bridge_impl!(StaticDef, crate::mir::mono::StaticDef); 293 | 294 | impl rustc_public_bridge::bridge::Prov for crate::ty::Prov { 295 | fn new(aid: crate::mir::alloc::AllocId) -> Self { 296 | Self(aid) 297 | } 298 | } 299 | 300 | impl rustc_public_bridge::bridge::Allocation 301 | for crate::ty::Allocation 302 | { 303 | fn new<'tcx>( 304 | bytes: Vec>, 305 | ptrs: Vec<(usize, rustc_middle::mir::interpret::AllocId)>, 306 | align: u64, 307 | mutability: rustc_middle::mir::Mutability, 308 | tables: &mut Tables<'tcx, compiler_interface::BridgeTys>, 309 | cx: &CompilerCtxt<'tcx, compiler_interface::BridgeTys>, 310 | ) -> Self { 311 | Self { 312 | bytes, 313 | provenance: ProvenanceMap { 314 | ptrs: ptrs.iter().map(|(i, aid)| (*i, tables.prov(*aid))).collect(), 315 | }, 316 | align, 317 | mutability: mutability.stable(tables, cx), 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /rustc_public/src/rustc_internal/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module that implements the bridge between rustc_public's IR and internal compiler MIR. 2 | //! 3 | //! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs 4 | //! until rustc_public's IR is complete. 5 | 6 | use std::cell::{Cell, RefCell}; 7 | 8 | use rustc_middle::ty::TyCtxt; 9 | use rustc_public_bridge::context::CompilerCtxt; 10 | use rustc_public_bridge::{Bridge, Container, Tables}; 11 | use rustc_span::def_id::CrateNum; 12 | use scoped_tls::scoped_thread_local; 13 | 14 | use crate::Error; 15 | use crate::unstable::{RustcInternal, Stable}; 16 | 17 | pub mod pretty; 18 | 19 | /// Convert an internal Rust compiler item into its stable counterpart, if one exists. 20 | /// 21 | /// # Warning 22 | /// 23 | /// This function is unstable, and its behavior may change at any point. 24 | /// E.g.: Items that were previously supported, may no longer be supported, or its translation may 25 | /// change. 26 | /// 27 | /// # Panics 28 | /// 29 | /// This function will panic if rustc_public has not been properly initialized. 30 | pub fn stable<'tcx, S: Stable<'tcx>>(item: S) -> S::T { 31 | with_container(|tables, cx| item.stable(tables, cx)) 32 | } 33 | 34 | /// Convert a stable item into its internal Rust compiler counterpart, if one exists. 35 | /// 36 | /// # Warning 37 | /// 38 | /// This function is unstable, and it's behavior may change at any point. 39 | /// Not every stable item can be converted to an internal one. 40 | /// Furthermore, items that were previously supported, may no longer be supported in newer versions. 41 | /// 42 | /// # Panics 43 | /// 44 | /// This function will panic if rustc_public has not been properly initialized. 45 | pub fn internal<'tcx, S>(tcx: TyCtxt<'tcx>, item: S) -> S::T<'tcx> 46 | where 47 | S: RustcInternal, 48 | { 49 | // The tcx argument ensures that the item won't outlive the type context. 50 | // See https://github.com/rust-lang/rust/pull/120128/commits/9aace6723572438a94378451793ca37deb768e72 51 | // for more details. 52 | with_container(|tables, _| item.internal(tables, tcx)) 53 | } 54 | 55 | pub fn crate_num(item: &crate::Crate) -> CrateNum { 56 | item.id.into() 57 | } 58 | 59 | // A thread local variable that stores a pointer to the tables mapping between TyCtxt 60 | // datastructures and rustc_public's IR datastructures 61 | scoped_thread_local! (static TLV: Cell<*const ()>); 62 | 63 | pub(crate) fn init<'tcx, F, T, B: Bridge>(container: &Container<'tcx, B>, f: F) -> T 64 | where 65 | F: FnOnce() -> T, 66 | { 67 | assert!(!TLV.is_set()); 68 | let ptr = container as *const _ as *const (); 69 | TLV.set(&Cell::new(ptr), || f()) 70 | } 71 | 72 | /// Loads the current context and calls a function with it. 73 | /// Do not nest these, as that will ICE. 74 | pub(crate) fn with_container( 75 | f: impl for<'tcx> FnOnce(&mut Tables<'tcx, B>, &CompilerCtxt<'tcx, B>) -> R, 76 | ) -> R { 77 | assert!(TLV.is_set()); 78 | TLV.with(|tlv| { 79 | let ptr = tlv.get(); 80 | assert!(!ptr.is_null()); 81 | let container = ptr as *const Container<'_, B>; 82 | let mut tables = unsafe { (*container).tables.borrow_mut() }; 83 | let cx = unsafe { (*container).cx.borrow() }; 84 | f(&mut *tables, &*cx) 85 | }) 86 | } 87 | 88 | pub fn run(tcx: TyCtxt<'_>, f: F) -> Result 89 | where 90 | F: FnOnce() -> T, 91 | { 92 | let compiler_cx = RefCell::new(CompilerCtxt::new(tcx)); 93 | let container = Container { tables: RefCell::new(Tables::default()), cx: compiler_cx }; 94 | 95 | crate::compiler_interface::run(&container, || init(&container, f)) 96 | } 97 | 98 | /// Instantiate and run the compiler with the provided arguments and callback. 99 | /// 100 | /// The callback will be invoked after the compiler ran all its analyses, but before code generation. 101 | /// Note that this macro accepts two different formats for the callback: 102 | /// 1. An ident that resolves to a function that accepts no argument and returns `ControlFlow` 103 | /// ```ignore(needs-extern-crate) 104 | /// # extern crate rustc_driver; 105 | /// # extern crate rustc_interface; 106 | /// # extern crate rustc_middle; 107 | /// # #[macro_use] 108 | /// # extern crate rustc_public; 109 | /// # 110 | /// # fn main() { 111 | /// # use std::ops::ControlFlow; 112 | /// # use rustc_public::CompilerError; 113 | /// fn analyze_code() -> ControlFlow<(), ()> { 114 | /// // Your code goes in here. 115 | /// # ControlFlow::Continue(()) 116 | /// } 117 | /// # let args = &["--verbose".to_string()]; 118 | /// let result = run!(args, analyze_code); 119 | /// # assert_eq!(result, Err(CompilerError::Skipped)) 120 | /// # } 121 | /// ``` 122 | /// 2. A closure expression: 123 | /// ```ignore(needs-extern-crate) 124 | /// # extern crate rustc_driver; 125 | /// # extern crate rustc_interface; 126 | /// # extern crate rustc_middle; 127 | /// # #[macro_use] 128 | /// # extern crate rustc_public; 129 | /// # 130 | /// # fn main() { 131 | /// # use std::ops::ControlFlow; 132 | /// # use rustc_public::CompilerError; 133 | /// fn analyze_code(extra_args: Vec) -> ControlFlow<(), ()> { 134 | /// # let _ = extra_args; 135 | /// // Your code goes in here. 136 | /// # ControlFlow::Continue(()) 137 | /// } 138 | /// # let args = &["--verbose".to_string()]; 139 | /// # let extra_args = vec![]; 140 | /// let result = run!(args, || analyze_code(extra_args)); 141 | /// # assert_eq!(result, Err(CompilerError::Skipped)) 142 | /// # } 143 | /// ``` 144 | #[macro_export] 145 | macro_rules! run { 146 | ($args:expr, $callback_fn:ident) => { 147 | $crate::run_driver!($args, || $callback_fn()) 148 | }; 149 | ($args:expr, $callback:expr) => { 150 | $crate::run_driver!($args, $callback) 151 | }; 152 | } 153 | 154 | /// Instantiate and run the compiler with the provided arguments and callback. 155 | /// 156 | /// This is similar to `run` but it invokes the callback with the compiler's `TyCtxt`, 157 | /// which can be used to invoke internal APIs. 158 | #[macro_export] 159 | macro_rules! run_with_tcx { 160 | ($args:expr, $callback_fn:ident) => { 161 | $crate::run_driver!($args, |tcx| $callback_fn(tcx), with_tcx) 162 | }; 163 | ($args:expr, $callback:expr) => { 164 | $crate::run_driver!($args, $callback, with_tcx) 165 | }; 166 | } 167 | 168 | /// Optionally include an ident. This is needed due to macro hygiene. 169 | #[macro_export] 170 | #[doc(hidden)] 171 | macro_rules! optional { 172 | (with_tcx $ident:ident) => { 173 | $ident 174 | }; 175 | } 176 | 177 | /// Prefer using [run!] and [run_with_tcx] instead. 178 | /// 179 | /// This macro implements the instantiation of a rustc_public driver, and it will invoke 180 | /// the given callback after the compiler analyses. 181 | /// 182 | /// The third argument determines whether the callback requires `tcx` as an argument. 183 | #[macro_export] 184 | #[doc(hidden)] 185 | macro_rules! run_driver { 186 | ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ 187 | use rustc_driver::{Callbacks, Compilation, run_compiler}; 188 | use rustc_middle::ty::TyCtxt; 189 | use rustc_interface::interface; 190 | use rustc_public::rustc_internal; 191 | use rustc_public::CompilerError; 192 | use std::ops::ControlFlow; 193 | 194 | pub struct RustcPublic ControlFlow> 195 | where 196 | B: Send, 197 | C: Send, 198 | F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, 199 | { 200 | callback: Option, 201 | result: Option>, 202 | } 203 | 204 | impl RustcPublic 205 | where 206 | B: Send, 207 | C: Send, 208 | F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, 209 | { 210 | /// Creates a new `RustcPublic` instance, with given test_function and arguments. 211 | pub fn new(callback: F) -> Self { 212 | RustcPublic { callback: Some(callback), result: None } 213 | } 214 | 215 | /// Runs the compiler against given target and tests it with `test_function` 216 | pub fn run(&mut self, args: &[String]) -> Result> { 217 | let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { 218 | run_compiler(&args, self); 219 | Ok(()) 220 | }); 221 | match (compiler_result, self.result.take()) { 222 | (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value), 223 | (Ok(Ok(())), Some(ControlFlow::Break(value))) => { 224 | Err(CompilerError::Interrupted(value)) 225 | } 226 | (Ok(Ok(_)), None) => Err(CompilerError::Skipped), 227 | // Two cases here: 228 | // - `run` finished normally and returned `Err` 229 | // - `run` panicked with `FatalErr` 230 | // You might think that normal compile errors cause the former, and 231 | // ICEs cause the latter. But some normal compiler errors also cause 232 | // the latter. So we can't meaningfully distinguish them, and group 233 | // them together. 234 | (Ok(Err(_)), _) | (Err(_), _) => Err(CompilerError::Failed), 235 | } 236 | } 237 | } 238 | 239 | impl Callbacks for RustcPublic 240 | where 241 | B: Send, 242 | C: Send, 243 | F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, 244 | { 245 | /// Called after analysis. Return value instructs the compiler whether to 246 | /// continue the compilation afterwards (defaults to `Compilation::Continue`) 247 | fn after_analysis<'tcx>( 248 | &mut self, 249 | _compiler: &interface::Compiler, 250 | tcx: TyCtxt<'tcx>, 251 | ) -> Compilation { 252 | if let Some(callback) = self.callback.take() { 253 | rustc_internal::run(tcx, || { 254 | self.result = Some(callback($($crate::optional!($with_tcx tcx))?)); 255 | }) 256 | .unwrap(); 257 | if self.result.as_ref().is_some_and(|val| val.is_continue()) { 258 | Compilation::Continue 259 | } else { 260 | Compilation::Stop 261 | } 262 | } else { 263 | Compilation::Continue 264 | } 265 | } 266 | } 267 | 268 | RustcPublic::new($callback).run($args) 269 | }}; 270 | } 271 | -------------------------------------------------------------------------------- /rustc_public/src/mir/mono.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::io; 3 | 4 | use rustc_public_bridge::bridge; 5 | use serde::Serialize; 6 | 7 | use crate::abi::FnAbi; 8 | use crate::crate_def::CrateDef; 9 | use crate::mir::Body; 10 | use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, Ty}; 11 | use crate::{CrateItem, DefId, Error, IndexedVal, ItemKind, Opaque, Symbol, with}; 12 | 13 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 14 | pub enum MonoItem { 15 | Fn(Instance), 16 | Static(StaticDef), 17 | GlobalAsm(Opaque), 18 | } 19 | 20 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize)] 21 | pub struct Instance { 22 | /// The type of instance. 23 | pub kind: InstanceKind, 24 | /// An ID used to get the instance definition from the compiler. 25 | /// Do not use this field directly. 26 | pub def: InstanceDef, 27 | } 28 | 29 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] 30 | pub enum InstanceKind { 31 | /// A user defined item. 32 | Item, 33 | /// A compiler intrinsic function. 34 | Intrinsic, 35 | /// A virtual function definition stored in a VTable. 36 | /// The `idx` field indicates the position in the VTable for this instance. 37 | Virtual { idx: usize }, 38 | /// A compiler generated shim. 39 | Shim, 40 | } 41 | 42 | impl Instance { 43 | /// Get the arguments this instance was instantiated with. 44 | pub fn args(&self) -> GenericArgs { 45 | with(|cx| cx.instance_args(self.def)) 46 | } 47 | 48 | /// Get the body of an Instance. 49 | /// 50 | /// The body will be eagerly monomorphized and all constants will already be evaluated. 51 | /// 52 | /// This method will return the intrinsic fallback body if one was defined. 53 | pub fn body(&self) -> Option { 54 | with(|context| context.instance_body(self.def)) 55 | } 56 | 57 | /// Check whether this instance has a body available. 58 | /// 59 | /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide 60 | /// whether to specialize the intrinsic or to use its fallback body. 61 | /// 62 | /// For more information on fallback body, see . 63 | /// 64 | /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build 65 | /// the rustc_public's IR body. 66 | pub fn has_body(&self) -> bool { 67 | with(|cx| cx.has_body(self.def.def_id())) 68 | } 69 | 70 | pub fn is_foreign_item(&self) -> bool { 71 | with(|cx| cx.is_foreign_item(self.def.def_id())) 72 | } 73 | 74 | /// Get the instance type with generic instantiations applied and lifetimes erased. 75 | pub fn ty(&self) -> Ty { 76 | with(|context| context.instance_ty(self.def)) 77 | } 78 | 79 | /// Retrieve information about this instance binary interface. 80 | pub fn fn_abi(&self) -> Result { 81 | with(|cx| cx.instance_abi(self.def)) 82 | } 83 | 84 | /// Retrieve the instance's mangled name used for calling the given instance. 85 | /// 86 | /// This will also look up the correct name of instances from upstream crates. 87 | pub fn mangled_name(&self) -> Symbol { 88 | with(|context| context.instance_mangled_name(self.def)) 89 | } 90 | 91 | /// Retrieve the instance name for diagnostic messages. 92 | /// 93 | /// This will return the specialized name, e.g., `std::vec::Vec::new`. 94 | pub fn name(&self) -> Symbol { 95 | with(|context| context.instance_name(self.def, false)) 96 | } 97 | 98 | /// Return a trimmed name of the given instance including its args. 99 | /// 100 | /// If a symbol name can only be imported from one place for a type, and as 101 | /// long as it was not glob-imported anywhere in the current crate, we trim its 102 | /// path and print only the name. 103 | pub fn trimmed_name(&self) -> Symbol { 104 | with(|context| context.instance_name(self.def, true)) 105 | } 106 | 107 | /// Retrieve the plain intrinsic name of an instance if it's an intrinsic. 108 | /// 109 | /// The plain name does not include type arguments (as `trimmed_name` does), 110 | /// which is more convenient to match with intrinsic symbols. 111 | pub fn intrinsic_name(&self) -> Option { 112 | match self.kind { 113 | InstanceKind::Intrinsic => { 114 | Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name())) 115 | } 116 | InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None, 117 | } 118 | } 119 | 120 | /// Resolve an instance starting from a function definition and generic arguments. 121 | pub fn resolve(def: FnDef, args: &GenericArgs) -> Result { 122 | with(|context| { 123 | context.resolve_instance(def, args).ok_or_else(|| { 124 | bridge::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) 125 | }) 126 | }) 127 | } 128 | 129 | /// Resolve the drop in place for a given type. 130 | pub fn resolve_drop_in_place(ty: Ty) -> Instance { 131 | with(|cx| cx.resolve_drop_in_place(ty)) 132 | } 133 | 134 | /// Resolve an instance for a given function pointer. 135 | pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { 136 | with(|context| { 137 | context.resolve_for_fn_ptr(def, args).ok_or_else(|| { 138 | bridge::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) 139 | }) 140 | }) 141 | } 142 | 143 | /// Resolve a closure with the expected kind. 144 | pub fn resolve_closure( 145 | def: ClosureDef, 146 | args: &GenericArgs, 147 | kind: ClosureKind, 148 | ) -> Result { 149 | with(|context| { 150 | context.resolve_closure(def, args, kind).ok_or_else(|| { 151 | bridge::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) 152 | }) 153 | }) 154 | } 155 | 156 | /// Check whether this instance is an empty shim. 157 | /// 158 | /// Allow users to check if this shim can be ignored when called directly. 159 | /// 160 | /// We have decided not to export different types of Shims to rustc_public users, however, this 161 | /// is a query that can be very helpful for users when processing DropGlue. 162 | /// 163 | /// When generating code for a Drop terminator, users can ignore an empty drop glue. 164 | /// These shims are only needed to generate a valid Drop call done via VTable. 165 | pub fn is_empty_shim(&self) -> bool { 166 | self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def)) 167 | } 168 | 169 | /// Try to constant evaluate the instance into a constant with the given type. 170 | /// 171 | /// This can be used to retrieve a constant that represents an intrinsic return such as 172 | /// `type_id`. 173 | pub fn try_const_eval(&self, const_ty: Ty) -> Result { 174 | with(|cx| cx.eval_instance(self.def, const_ty)) 175 | } 176 | 177 | /// Emit the body of this instance if it has one. 178 | pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { 179 | if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) } 180 | } 181 | } 182 | 183 | impl Debug for Instance { 184 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 185 | f.debug_struct("Instance") 186 | .field("kind", &self.kind) 187 | .field("def", &self.mangled_name()) 188 | .field("args", &self.args()) 189 | .finish() 190 | } 191 | } 192 | 193 | /// Try to convert a crate item into an instance. 194 | /// The item cannot be generic in order to be converted into an instance. 195 | impl TryFrom for Instance { 196 | type Error = crate::Error; 197 | 198 | fn try_from(item: CrateItem) -> Result { 199 | with(|context| { 200 | let def_id = item.def_id(); 201 | if !context.requires_monomorphization(def_id) { 202 | Ok(context.mono_instance(def_id)) 203 | } else { 204 | Err(bridge::Error::new("Item requires monomorphization".to_string())) 205 | } 206 | }) 207 | } 208 | } 209 | 210 | /// Try to convert an instance into a crate item. 211 | /// Only user defined instances can be converted. 212 | impl TryFrom for CrateItem { 213 | type Error = crate::Error; 214 | 215 | fn try_from(value: Instance) -> Result { 216 | with(|context| { 217 | if value.kind == InstanceKind::Item && context.has_body(value.def.def_id()) { 218 | Ok(CrateItem(context.instance_def_id(value.def))) 219 | } else { 220 | Err(bridge::Error::new(format!("Item kind `{:?}` cannot be converted", value.kind))) 221 | } 222 | }) 223 | } 224 | } 225 | 226 | impl From for MonoItem { 227 | fn from(value: Instance) -> Self { 228 | MonoItem::Fn(value) 229 | } 230 | } 231 | 232 | impl From for MonoItem { 233 | fn from(value: StaticDef) -> Self { 234 | MonoItem::Static(value) 235 | } 236 | } 237 | 238 | impl From for CrateItem { 239 | fn from(value: StaticDef) -> Self { 240 | CrateItem(value.0) 241 | } 242 | } 243 | 244 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] 245 | pub struct InstanceDef(usize); 246 | 247 | impl CrateDef for InstanceDef { 248 | fn def_id(&self) -> DefId { 249 | with(|context| context.instance_def_id(*self)) 250 | } 251 | } 252 | 253 | crate_def! { 254 | /// Holds information about a static variable definition. 255 | #[derive(Serialize)] 256 | pub StaticDef; 257 | } 258 | 259 | impl TryFrom for StaticDef { 260 | type Error = crate::Error; 261 | 262 | fn try_from(value: CrateItem) -> Result { 263 | if matches!(value.kind(), ItemKind::Static) { 264 | Ok(StaticDef(value.0)) 265 | } else { 266 | Err(bridge::Error::new(format!("Expected a static item, but found: {value:?}"))) 267 | } 268 | } 269 | } 270 | 271 | impl TryFrom for StaticDef { 272 | type Error = crate::Error; 273 | 274 | fn try_from(value: Instance) -> Result { 275 | StaticDef::try_from(CrateItem::try_from(value)?) 276 | } 277 | } 278 | 279 | impl From for Instance { 280 | fn from(value: StaticDef) -> Self { 281 | // A static definition should always be convertible to an instance. 282 | with(|cx| cx.mono_instance(value.def_id())) 283 | } 284 | } 285 | 286 | impl StaticDef { 287 | /// Return the type of this static definition. 288 | pub fn ty(&self) -> Ty { 289 | with(|cx| cx.def_ty(self.0)) 290 | } 291 | 292 | /// Evaluate a static's initializer, returning the allocation of the initializer's memory. 293 | pub fn eval_initializer(&self) -> Result { 294 | with(|cx| cx.eval_static_initializer(*self)) 295 | } 296 | } 297 | 298 | impl IndexedVal for InstanceDef { 299 | fn to_val(index: usize) -> Self { 300 | InstanceDef(index) 301 | } 302 | fn to_index(&self) -> usize { 303 | self.0 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /rustc_public/tests/print/operands.stdout: -------------------------------------------------------------------------------- 1 | // WARNING: This is highly experimental output it's intended for rustc_public developers only. 2 | // If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir. 3 | fn operands(_1: u8) -> () { 4 | let mut _0: (); 5 | let _2: [u8; 10]; 6 | let mut _3: u8; 7 | let _4: u8; 8 | let _5: usize; 9 | let mut _6: bool; 10 | let _7: u8; 11 | let _8: usize; 12 | let mut _9: (usize, bool); 13 | let mut _10: bool; 14 | let _11: (); 15 | let mut _12: (&u8, &u8); 16 | let mut _13: &u8; 17 | let mut _14: &u8; 18 | let _15: &u8; 19 | let _16: &u8; 20 | let mut _17: bool; 21 | let mut _18: u8; 22 | let mut _19: u8; 23 | let mut _20: !; 24 | let _21: core::panicking::AssertKind; 25 | let _22: !; 26 | let mut _23: core::panicking::AssertKind; 27 | let mut _24: &u8; 28 | let _25: &u8; 29 | let mut _26: &u8; 30 | let _27: &u8; 31 | let mut _28: std::option::Option>; 32 | let _29: &u8; 33 | let _30: u8; 34 | let _31: (); 35 | let mut _32: (&u8, &u8); 36 | let mut _33: &u8; 37 | let mut _34: &u8; 38 | let _35: &u8; 39 | let _36: &u8; 40 | let mut _37: bool; 41 | let mut _38: u8; 42 | let mut _39: u8; 43 | let mut _40: !; 44 | let _41: core::panicking::AssertKind; 45 | let _42: !; 46 | let mut _43: core::panicking::AssertKind; 47 | let mut _44: &u8; 48 | let _45: &u8; 49 | let mut _46: &u8; 50 | let _47: &u8; 51 | let mut _48: std::option::Option>; 52 | let _49: (u8, u8); 53 | let mut _50: u8; 54 | let mut _51: u8; 55 | let _52: u8; 56 | let _53: u8; 57 | let _54: (); 58 | let mut _55: (&u8, &u8); 59 | let mut _56: &u8; 60 | let mut _57: &u8; 61 | let _58: &u8; 62 | let _59: &u8; 63 | let mut _60: bool; 64 | let mut _61: u8; 65 | let mut _62: u8; 66 | let mut _63: !; 67 | let _64: core::panicking::AssertKind; 68 | let _65: !; 69 | let mut _66: core::panicking::AssertKind; 70 | let mut _67: &u8; 71 | let _68: &u8; 72 | let mut _69: &u8; 73 | let _70: &u8; 74 | let mut _71: std::option::Option>; 75 | let _72: usize; 76 | let mut _73: &[u8]; 77 | let mut _74: &[u8; 10]; 78 | let _75: usize; 79 | let mut _76: &usize; 80 | let _77: &usize; 81 | let _78: (); 82 | let mut _79: (&usize, &usize); 83 | let mut _80: &usize; 84 | let mut _81: &usize; 85 | let _82: &usize; 86 | let _83: &usize; 87 | let mut _84: bool; 88 | let mut _85: usize; 89 | let mut _86: usize; 90 | let mut _87: !; 91 | let _88: core::panicking::AssertKind; 92 | let _89: !; 93 | let mut _90: core::panicking::AssertKind; 94 | let mut _91: &usize; 95 | let _92: &usize; 96 | let mut _93: &usize; 97 | let _94: &usize; 98 | let mut _95: std::option::Option>; 99 | debug val => _1; 100 | debug array => _2; 101 | debug first => _4; 102 | debug last => _7; 103 | debug left_val => _15; 104 | debug right_val => _16; 105 | debug kind => _21; 106 | debug reference => _29; 107 | debug dereferenced => _30; 108 | debug left_val => _35; 109 | debug right_val => _36; 110 | debug kind => _41; 111 | debug tuple => _49; 112 | debug first_again => _52; 113 | debug first_again_again => _53; 114 | debug left_val => _58; 115 | debug right_val => _59; 116 | debug kind => _64; 117 | debug length => _72; 118 | debug size_of => _75; 119 | debug left_val => _82; 120 | debug right_val => _83; 121 | debug kind => _88; 122 | bb0: { 123 | StorageLive(_2); 124 | StorageLive(_3); 125 | _3 = _1; 126 | _2 = [move _3; 10]; 127 | StorageDead(_3); 128 | StorageLive(_4); 129 | StorageLive(_5); 130 | _5 = 0_usize; 131 | _6 = Lt(_5, 10_usize); 132 | assert(move _6, "index out of bounds: the length is {} but the index is {}", 10_usize, _5) -> [success: bb1, unwind unreachable]; 133 | } 134 | bb1: { 135 | _4 = _2[_5]; 136 | StorageDead(_5); 137 | StorageLive(_7); 138 | StorageLive(_8); 139 | _9 = CheckedSub(10_usize, 1_usize); 140 | assert(!move (_9.1: bool), "attempt to compute `{} - {}`, which would overflow", 10_usize, 1_usize) -> [success: bb2, unwind unreachable]; 141 | } 142 | bb2: { 143 | _8 = move (_9.0: usize); 144 | _10 = Lt(_8, 10_usize); 145 | assert(move _10, "index out of bounds: the length is {} but the index is {}", 10_usize, _8) -> [success: bb3, unwind unreachable]; 146 | } 147 | bb3: { 148 | _7 = _2[_8]; 149 | StorageDead(_8); 150 | StorageLive(_11); 151 | StorageLive(_12); 152 | StorageLive(_13); 153 | _13 = &_4; 154 | StorageLive(_14); 155 | _14 = &_7; 156 | _12 = (move _13, move _14); 157 | StorageDead(_14); 158 | StorageDead(_13); 159 | StorageLive(_15); 160 | _15 = (_12.0: &u8); 161 | StorageLive(_16); 162 | _16 = (_12.1: &u8); 163 | StorageLive(_17); 164 | StorageLive(_18); 165 | _18 = (*_15); 166 | StorageLive(_19); 167 | _19 = (*_16); 168 | _17 = Eq(move _18, move _19); 169 | switchInt(move _17) -> [0: bb5, otherwise: bb4]; 170 | } 171 | bb4: { 172 | StorageDead(_19); 173 | StorageDead(_18); 174 | _11 = (); 175 | StorageDead(_17); 176 | StorageDead(_16); 177 | StorageDead(_15); 178 | StorageDead(_12); 179 | StorageDead(_11); 180 | StorageLive(_29); 181 | _29 = &_4; 182 | StorageLive(_30); 183 | _30 = (*_29); 184 | StorageLive(_31); 185 | StorageLive(_32); 186 | StorageLive(_33); 187 | _33 = &_30; 188 | StorageLive(_34); 189 | _34 = &_4; 190 | _32 = (move _33, move _34); 191 | StorageDead(_34); 192 | StorageDead(_33); 193 | StorageLive(_35); 194 | _35 = (_32.0: &u8); 195 | StorageLive(_36); 196 | _36 = (_32.1: &u8); 197 | StorageLive(_37); 198 | StorageLive(_38); 199 | _38 = (*_35); 200 | StorageLive(_39); 201 | _39 = (*_36); 202 | _37 = Eq(move _38, move _39); 203 | switchInt(move _37) -> [0: bb7, otherwise: bb6]; 204 | } 205 | bb5: { 206 | StorageDead(_19); 207 | StorageDead(_18); 208 | StorageLive(_21); 209 | _21 = core::panicking::AssertKind::Eq; 210 | StorageLive(_22); 211 | StorageLive(_23); 212 | _23 = move _21; 213 | StorageLive(_24); 214 | StorageLive(_25); 215 | _25 = &(*_15); 216 | _24 = &(*_25); 217 | StorageLive(_26); 218 | StorageLive(_27); 219 | _27 = &(*_16); 220 | _26 = &(*_27); 221 | StorageLive(_28); 222 | _28 = std::option::Option::None; 223 | _22 = core::panicking::assert_failed::(move _23, move _24, move _26, move _28) -> unwind unreachable; 224 | } 225 | bb6: { 226 | StorageDead(_39); 227 | StorageDead(_38); 228 | _31 = (); 229 | StorageDead(_37); 230 | StorageDead(_36); 231 | StorageDead(_35); 232 | StorageDead(_32); 233 | StorageDead(_31); 234 | StorageLive(_49); 235 | StorageLive(_50); 236 | _50 = _4; 237 | StorageLive(_51); 238 | _51 = _7; 239 | _49 = (move _50, move _51); 240 | StorageDead(_51); 241 | StorageDead(_50); 242 | StorageLive(_52); 243 | _52 = (_49.0: u8); 244 | StorageLive(_53); 245 | _53 = (_49.0: u8); 246 | StorageLive(_54); 247 | StorageLive(_55); 248 | StorageLive(_56); 249 | _56 = &_52; 250 | StorageLive(_57); 251 | _57 = &_53; 252 | _55 = (move _56, move _57); 253 | StorageDead(_57); 254 | StorageDead(_56); 255 | StorageLive(_58); 256 | _58 = (_55.0: &u8); 257 | StorageLive(_59); 258 | _59 = (_55.1: &u8); 259 | StorageLive(_60); 260 | StorageLive(_61); 261 | _61 = (*_58); 262 | StorageLive(_62); 263 | _62 = (*_59); 264 | _60 = Eq(move _61, move _62); 265 | switchInt(move _60) -> [0: bb9, otherwise: bb8]; 266 | } 267 | bb7: { 268 | StorageDead(_39); 269 | StorageDead(_38); 270 | StorageLive(_41); 271 | _41 = core::panicking::AssertKind::Eq; 272 | StorageLive(_42); 273 | StorageLive(_43); 274 | _43 = move _41; 275 | StorageLive(_44); 276 | StorageLive(_45); 277 | _45 = &(*_35); 278 | _44 = &(*_45); 279 | StorageLive(_46); 280 | StorageLive(_47); 281 | _47 = &(*_36); 282 | _46 = &(*_47); 283 | StorageLive(_48); 284 | _48 = std::option::Option::None; 285 | _42 = core::panicking::assert_failed::(move _43, move _44, move _46, move _48) -> unwind unreachable; 286 | } 287 | bb8: { 288 | StorageDead(_62); 289 | StorageDead(_61); 290 | _54 = (); 291 | StorageDead(_60); 292 | StorageDead(_59); 293 | StorageDead(_58); 294 | StorageDead(_55); 295 | StorageDead(_54); 296 | StorageLive(_72); 297 | StorageLive(_73); 298 | StorageLive(_74); 299 | _74 = &_2; 300 | _73 = move _74 as &[u8]; 301 | StorageDead(_74); 302 | _72 = core::slice::::len(move _73) -> [return: bb10, unwind unreachable]; 303 | } 304 | bb9: { 305 | StorageDead(_62); 306 | StorageDead(_61); 307 | StorageLive(_64); 308 | _64 = core::panicking::AssertKind::Eq; 309 | StorageLive(_65); 310 | StorageLive(_66); 311 | _66 = move _64; 312 | StorageLive(_67); 313 | StorageLive(_68); 314 | _68 = &(*_58); 315 | _67 = &(*_68); 316 | StorageLive(_69); 317 | StorageLive(_70); 318 | _70 = &(*_59); 319 | _69 = &(*_70); 320 | StorageLive(_71); 321 | _71 = std::option::Option::None; 322 | _65 = core::panicking::assert_failed::(move _66, move _67, move _69, move _71) -> unwind unreachable; 323 | } 324 | bb10: { 325 | StorageDead(_73); 326 | StorageLive(_75); 327 | StorageLive(_76); 328 | StorageLive(_77); 329 | _77 = &_72; 330 | _76 = &(*_77); 331 | _75 = std::mem::size_of_val::(move _76) -> [return: bb11, unwind unreachable]; 332 | } 333 | bb11: { 334 | StorageDead(_76); 335 | StorageDead(_77); 336 | StorageLive(_78); 337 | StorageLive(_79); 338 | StorageLive(_80); 339 | _80 = &_72; 340 | StorageLive(_81); 341 | _81 = &_75; 342 | _79 = (move _80, move _81); 343 | StorageDead(_81); 344 | StorageDead(_80); 345 | StorageLive(_82); 346 | _82 = (_79.0: &usize); 347 | StorageLive(_83); 348 | _83 = (_79.1: &usize); 349 | StorageLive(_84); 350 | StorageLive(_85); 351 | _85 = (*_82); 352 | StorageLive(_86); 353 | _86 = (*_83); 354 | _84 = Eq(move _85, move _86); 355 | switchInt(move _84) -> [0: bb13, otherwise: bb12]; 356 | } 357 | bb12: { 358 | StorageDead(_86); 359 | StorageDead(_85); 360 | _78 = (); 361 | StorageDead(_84); 362 | StorageDead(_83); 363 | StorageDead(_82); 364 | StorageDead(_79); 365 | StorageDead(_78); 366 | _0 = (); 367 | StorageDead(_75); 368 | StorageDead(_72); 369 | StorageDead(_53); 370 | StorageDead(_52); 371 | StorageDead(_49); 372 | StorageDead(_30); 373 | StorageDead(_29); 374 | StorageDead(_7); 375 | StorageDead(_4); 376 | StorageDead(_2); 377 | return; 378 | } 379 | bb13: { 380 | StorageDead(_86); 381 | StorageDead(_85); 382 | StorageLive(_88); 383 | _88 = core::panicking::AssertKind::Eq; 384 | StorageLive(_89); 385 | StorageLive(_90); 386 | _90 = move _88; 387 | StorageLive(_91); 388 | StorageLive(_92); 389 | _92 = &(*_82); 390 | _91 = &(*_92); 391 | StorageLive(_93); 392 | StorageLive(_94); 393 | _94 = &(*_83); 394 | _93 = &(*_94); 395 | StorageLive(_95); 396 | _95 = std::option::Option::None; 397 | _89 = core::panicking::assert_failed::(move _90, move _91, move _93, move _95) -> unwind unreachable; 398 | } 399 | } 400 | fn operands::{constant#0}() -> usize { 401 | let mut _0: usize; 402 | bb0: { 403 | _0 = 10_usize; 404 | return; 405 | } 406 | } 407 | fn more_operands() -> [Ctors; 3] { 408 | let mut _0: [Ctors; 3]; 409 | let _1: Dummy; 410 | let _2: Ctors; 411 | let _3: Ctors; 412 | let mut _4: Dummy; 413 | let _5: Ctors; 414 | let mut _6: Ctors; 415 | let mut _7: Ctors; 416 | let mut _8: Ctors; 417 | debug dummy => _1; 418 | debug unit => _2; 419 | debug struct_like => _3; 420 | debug tup_like => _5; 421 | bb0: { 422 | StorageLive(_1); 423 | _1 = Dummy('a', core::num::::MIN); 424 | StorageLive(_2); 425 | _2 = Ctors::Unit; 426 | StorageLive(_3); 427 | StorageLive(_4); 428 | _4 = move _1; 429 | _3 = Ctors::StructLike(move _4); 430 | StorageDead(_4); 431 | StorageLive(_5); 432 | _5 = Ctors::TupLike(false); 433 | StorageLive(_6); 434 | _6 = move _2; 435 | StorageLive(_7); 436 | _7 = move _3; 437 | StorageLive(_8); 438 | _8 = move _5; 439 | _0 = [move _6, move _7, move _8]; 440 | StorageDead(_8); 441 | StorageDead(_7); 442 | StorageDead(_6); 443 | StorageDead(_5); 444 | StorageDead(_3); 445 | StorageDead(_2); 446 | StorageDead(_1); 447 | return; 448 | } 449 | } 450 | fn more_operands::{constant#0}() -> usize { 451 | let mut _0: usize; 452 | bb0: { 453 | _0 = 3_usize; 454 | return; 455 | } 456 | } 457 | fn closures(_1: bool, _2: bool) -> {closure@$DIR/operands.rs:47:5: 47:19} { 458 | let mut _0: {closure@$DIR/operands.rs:47:5: 47:19}; 459 | debug x => _1; 460 | debug z => _2; 461 | bb0: { 462 | _0 = {closure@$DIR/operands.rs:47:5: 47:19}(_1, _2); 463 | return; 464 | } 465 | } 466 | fn closures::{closure#0}(_1: {closure@$DIR/operands.rs:47:5: 47:19}, _2: bool) -> bool { 467 | let mut _0: bool; 468 | let mut _3: bool; 469 | let mut _4: bool; 470 | let mut _5: bool; 471 | debug y => _2; 472 | debug x => (_1.0: bool); 473 | debug z => (_1.1: bool); 474 | bb0: { 475 | StorageLive(_3); 476 | StorageLive(_4); 477 | _4 = (_1.0: bool); 478 | StorageLive(_5); 479 | _5 = _2; 480 | _3 = BitXor(move _4, move _5); 481 | switchInt(move _3) -> [0: bb2, otherwise: bb1]; 482 | } 483 | bb1: { 484 | StorageDead(_5); 485 | StorageDead(_4); 486 | _0 = true; 487 | goto -> bb3; 488 | } 489 | bb2: { 490 | StorageDead(_5); 491 | StorageDead(_4); 492 | _0 = (_1.1: bool); 493 | goto -> bb3; 494 | } 495 | bb3: { 496 | StorageDead(_3); 497 | return; 498 | } 499 | } 500 | fn Ctors::TupLike(_1: bool) -> Ctors { 501 | let mut _0: Ctors; 502 | bb0: { 503 | _0 = Ctors::TupLike(move _1); 504 | return; 505 | } 506 | } 507 | -------------------------------------------------------------------------------- /rustc_public/src/unstable/convert/stable/abi.rs: -------------------------------------------------------------------------------- 1 | //! Conversion of internal Rust compiler `rustc_target` and `rustc_abi` items to stable ones. 2 | 3 | #![allow(rustc::usage_of_qualified_ty)] 4 | 5 | use rustc_abi::{ArmCall, CanonAbi, InterruptKind, X86Call}; 6 | use rustc_middle::ty; 7 | use rustc_public_bridge::Tables; 8 | use rustc_public_bridge::context::CompilerCtxt; 9 | use rustc_target::callconv; 10 | 11 | use crate::abi::{ 12 | AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, 13 | IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar, 14 | TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange, 15 | }; 16 | use crate::compiler_interface::BridgeTys; 17 | use crate::target::MachineSize as Size; 18 | use crate::ty::{Align, VariantIdx}; 19 | use crate::unstable::Stable; 20 | use crate::{IndexedVal, opaque}; 21 | 22 | impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx { 23 | type T = VariantIdx; 24 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 25 | VariantIdx::to_val(self.as_usize()) 26 | } 27 | } 28 | 29 | impl<'tcx> Stable<'tcx> for rustc_abi::Endian { 30 | type T = crate::target::Endian; 31 | 32 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 33 | match self { 34 | rustc_abi::Endian::Little => crate::target::Endian::Little, 35 | rustc_abi::Endian::Big => crate::target::Endian::Big, 36 | } 37 | } 38 | } 39 | 40 | impl<'tcx> Stable<'tcx> for rustc_abi::TyAndLayout<'tcx, ty::Ty<'tcx>> { 41 | type T = TyAndLayout; 42 | 43 | fn stable<'cx>( 44 | &self, 45 | tables: &mut Tables<'cx, BridgeTys>, 46 | cx: &CompilerCtxt<'cx, BridgeTys>, 47 | ) -> Self::T { 48 | TyAndLayout { ty: self.ty.stable(tables, cx), layout: self.layout.stable(tables, cx) } 49 | } 50 | } 51 | 52 | impl<'tcx> Stable<'tcx> for rustc_abi::Layout<'tcx> { 53 | type T = Layout; 54 | 55 | fn stable<'cx>( 56 | &self, 57 | tables: &mut Tables<'cx, BridgeTys>, 58 | cx: &CompilerCtxt<'cx, BridgeTys>, 59 | ) -> Self::T { 60 | tables.layout_id(cx.lift(*self).unwrap()) 61 | } 62 | } 63 | 64 | impl<'tcx> Stable<'tcx> for rustc_abi::LayoutData { 65 | type T = LayoutShape; 66 | 67 | fn stable<'cx>( 68 | &self, 69 | tables: &mut Tables<'cx, BridgeTys>, 70 | cx: &CompilerCtxt<'cx, BridgeTys>, 71 | ) -> Self::T { 72 | LayoutShape { 73 | fields: self.fields.stable(tables, cx), 74 | variants: self.variants.stable(tables, cx), 75 | abi: self.backend_repr.stable(tables, cx), 76 | abi_align: self.align.abi.stable(tables, cx), 77 | size: self.size.stable(tables, cx), 78 | } 79 | } 80 | } 81 | 82 | impl<'tcx> Stable<'tcx> for callconv::FnAbi<'tcx, ty::Ty<'tcx>> { 83 | type T = FnAbi; 84 | 85 | fn stable<'cx>( 86 | &self, 87 | tables: &mut Tables<'cx, BridgeTys>, 88 | cx: &CompilerCtxt<'cx, BridgeTys>, 89 | ) -> Self::T { 90 | assert!(self.args.len() >= self.fixed_count as usize); 91 | assert!(!self.c_variadic || matches!(self.conv, CanonAbi::C)); 92 | FnAbi { 93 | args: self.args.as_ref().stable(tables, cx), 94 | ret: self.ret.stable(tables, cx), 95 | fixed_count: self.fixed_count, 96 | conv: self.conv.stable(tables, cx), 97 | c_variadic: self.c_variadic, 98 | } 99 | } 100 | } 101 | 102 | impl<'tcx> Stable<'tcx> for callconv::ArgAbi<'tcx, ty::Ty<'tcx>> { 103 | type T = ArgAbi; 104 | 105 | fn stable<'cx>( 106 | &self, 107 | tables: &mut Tables<'cx, BridgeTys>, 108 | cx: &CompilerCtxt<'cx, BridgeTys>, 109 | ) -> Self::T { 110 | ArgAbi { 111 | ty: self.layout.ty.stable(tables, cx), 112 | layout: self.layout.layout.stable(tables, cx), 113 | mode: self.mode.stable(tables, cx), 114 | } 115 | } 116 | } 117 | 118 | impl<'tcx> Stable<'tcx> for CanonAbi { 119 | type T = CallConvention; 120 | 121 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 122 | match self { 123 | CanonAbi::C => CallConvention::C, 124 | CanonAbi::Rust => CallConvention::Rust, 125 | CanonAbi::RustCold => CallConvention::Cold, 126 | CanonAbi::Custom => CallConvention::Custom, 127 | CanonAbi::Arm(arm_call) => match arm_call { 128 | ArmCall::Aapcs => CallConvention::ArmAapcs, 129 | ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall, 130 | ArmCall::CCmseNonSecureEntry => CallConvention::CCmseNonSecureEntry, 131 | }, 132 | CanonAbi::GpuKernel => CallConvention::GpuKernel, 133 | CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { 134 | InterruptKind::Avr => CallConvention::AvrInterrupt, 135 | InterruptKind::AvrNonBlocking => CallConvention::AvrNonBlockingInterrupt, 136 | InterruptKind::Msp430 => CallConvention::Msp430Intr, 137 | InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => { 138 | CallConvention::RiscvInterrupt 139 | } 140 | InterruptKind::X86 => CallConvention::X86Intr, 141 | }, 142 | CanonAbi::X86(x86_call) => match x86_call { 143 | X86Call::Fastcall => CallConvention::X86Fastcall, 144 | X86Call::Stdcall => CallConvention::X86Stdcall, 145 | X86Call::SysV64 => CallConvention::X86_64SysV, 146 | X86Call::Thiscall => CallConvention::X86ThisCall, 147 | X86Call::Vectorcall => CallConvention::X86VectorCall, 148 | X86Call::Win64 => CallConvention::X86_64Win64, 149 | }, 150 | } 151 | } 152 | } 153 | 154 | impl<'tcx> Stable<'tcx> for callconv::PassMode { 155 | type T = PassMode; 156 | 157 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 158 | match self { 159 | callconv::PassMode::Ignore => PassMode::Ignore, 160 | callconv::PassMode::Direct(attr) => PassMode::Direct(opaque(attr)), 161 | callconv::PassMode::Pair(first, second) => { 162 | PassMode::Pair(opaque(first), opaque(second)) 163 | } 164 | callconv::PassMode::Cast { pad_i32, cast } => { 165 | PassMode::Cast { pad_i32: *pad_i32, cast: opaque(cast) } 166 | } 167 | callconv::PassMode::Indirect { attrs, meta_attrs, on_stack } => PassMode::Indirect { 168 | attrs: opaque(attrs), 169 | meta_attrs: opaque(meta_attrs), 170 | on_stack: *on_stack, 171 | }, 172 | } 173 | } 174 | } 175 | 176 | impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape { 177 | type T = FieldsShape; 178 | 179 | fn stable<'cx>( 180 | &self, 181 | tables: &mut Tables<'cx, BridgeTys>, 182 | cx: &CompilerCtxt<'cx, BridgeTys>, 183 | ) -> Self::T { 184 | match self { 185 | rustc_abi::FieldsShape::Primitive => FieldsShape::Primitive, 186 | rustc_abi::FieldsShape::Union(count) => FieldsShape::Union(*count), 187 | rustc_abi::FieldsShape::Array { stride, count } => { 188 | FieldsShape::Array { stride: stride.stable(tables, cx), count: *count } 189 | } 190 | rustc_abi::FieldsShape::Arbitrary { offsets, .. } => { 191 | FieldsShape::Arbitrary { offsets: offsets.iter().as_slice().stable(tables, cx) } 192 | } 193 | } 194 | } 195 | } 196 | 197 | impl<'tcx> Stable<'tcx> for rustc_abi::Variants { 198 | type T = VariantsShape; 199 | 200 | fn stable<'cx>( 201 | &self, 202 | tables: &mut Tables<'cx, BridgeTys>, 203 | cx: &CompilerCtxt<'cx, BridgeTys>, 204 | ) -> Self::T { 205 | match self { 206 | rustc_abi::Variants::Single { index } => { 207 | VariantsShape::Single { index: index.stable(tables, cx) } 208 | } 209 | rustc_abi::Variants::Empty => VariantsShape::Empty, 210 | rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => { 211 | VariantsShape::Multiple { 212 | tag: tag.stable(tables, cx), 213 | tag_encoding: tag_encoding.stable(tables, cx), 214 | tag_field: tag_field.stable(tables, cx), 215 | variants: variants.iter().as_slice().stable(tables, cx), 216 | } 217 | } 218 | } 219 | } 220 | } 221 | 222 | impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding { 223 | type T = TagEncoding; 224 | 225 | fn stable<'cx>( 226 | &self, 227 | tables: &mut Tables<'cx, BridgeTys>, 228 | cx: &CompilerCtxt<'cx, BridgeTys>, 229 | ) -> Self::T { 230 | match self { 231 | rustc_abi::TagEncoding::Direct => TagEncoding::Direct, 232 | rustc_abi::TagEncoding::Niche { untagged_variant, niche_variants, niche_start } => { 233 | TagEncoding::Niche { 234 | untagged_variant: untagged_variant.stable(tables, cx), 235 | niche_variants: niche_variants.stable(tables, cx), 236 | niche_start: *niche_start, 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { 244 | type T = ValueAbi; 245 | 246 | fn stable<'cx>( 247 | &self, 248 | tables: &mut Tables<'cx, BridgeTys>, 249 | cx: &CompilerCtxt<'cx, BridgeTys>, 250 | ) -> Self::T { 251 | match *self { 252 | rustc_abi::BackendRepr::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables, cx)), 253 | rustc_abi::BackendRepr::ScalarPair(first, second) => { 254 | ValueAbi::ScalarPair(first.stable(tables, cx), second.stable(tables, cx)) 255 | } 256 | rustc_abi::BackendRepr::SimdVector { element, count } => { 257 | ValueAbi::Vector { element: element.stable(tables, cx), count } 258 | } 259 | rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized }, 260 | } 261 | } 262 | } 263 | 264 | impl<'tcx> Stable<'tcx> for rustc_abi::Size { 265 | type T = Size; 266 | 267 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 268 | Size::from_bits(self.bits_usize()) 269 | } 270 | } 271 | 272 | impl<'tcx> Stable<'tcx> for rustc_abi::Align { 273 | type T = Align; 274 | 275 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 276 | self.bytes() 277 | } 278 | } 279 | 280 | impl<'tcx> Stable<'tcx> for rustc_abi::Scalar { 281 | type T = Scalar; 282 | 283 | fn stable<'cx>( 284 | &self, 285 | tables: &mut Tables<'cx, BridgeTys>, 286 | cx: &CompilerCtxt<'cx, BridgeTys>, 287 | ) -> Self::T { 288 | match self { 289 | rustc_abi::Scalar::Initialized { value, valid_range } => Scalar::Initialized { 290 | value: value.stable(tables, cx), 291 | valid_range: valid_range.stable(tables, cx), 292 | }, 293 | rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables, cx) }, 294 | } 295 | } 296 | } 297 | 298 | impl<'tcx> Stable<'tcx> for rustc_abi::Primitive { 299 | type T = Primitive; 300 | 301 | fn stable<'cx>( 302 | &self, 303 | tables: &mut Tables<'cx, BridgeTys>, 304 | cx: &CompilerCtxt<'cx, BridgeTys>, 305 | ) -> Self::T { 306 | match self { 307 | rustc_abi::Primitive::Int(length, signed) => { 308 | Primitive::Int { length: length.stable(tables, cx), signed: *signed } 309 | } 310 | rustc_abi::Primitive::Float(length) => { 311 | Primitive::Float { length: length.stable(tables, cx) } 312 | } 313 | rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables, cx)), 314 | } 315 | } 316 | } 317 | 318 | impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace { 319 | type T = AddressSpace; 320 | 321 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 322 | AddressSpace(self.0) 323 | } 324 | } 325 | 326 | impl<'tcx> Stable<'tcx> for rustc_abi::Integer { 327 | type T = IntegerLength; 328 | 329 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 330 | match self { 331 | rustc_abi::Integer::I8 => IntegerLength::I8, 332 | rustc_abi::Integer::I16 => IntegerLength::I16, 333 | rustc_abi::Integer::I32 => IntegerLength::I32, 334 | rustc_abi::Integer::I64 => IntegerLength::I64, 335 | rustc_abi::Integer::I128 => IntegerLength::I128, 336 | } 337 | } 338 | } 339 | 340 | impl<'tcx> Stable<'tcx> for rustc_abi::Float { 341 | type T = FloatLength; 342 | 343 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 344 | match self { 345 | rustc_abi::Float::F16 => FloatLength::F16, 346 | rustc_abi::Float::F32 => FloatLength::F32, 347 | rustc_abi::Float::F64 => FloatLength::F64, 348 | rustc_abi::Float::F128 => FloatLength::F128, 349 | } 350 | } 351 | } 352 | 353 | impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange { 354 | type T = WrappingRange; 355 | 356 | fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { 357 | WrappingRange { start: self.start, end: self.end } 358 | } 359 | } 360 | 361 | impl<'tcx> Stable<'tcx> for rustc_abi::ReprFlags { 362 | type T = ReprFlags; 363 | 364 | fn stable<'cx>( 365 | &self, 366 | _tables: &mut Tables<'cx, BridgeTys>, 367 | _cx: &CompilerCtxt<'cx, BridgeTys>, 368 | ) -> Self::T { 369 | ReprFlags { 370 | is_simd: self.intersects(Self::IS_SIMD), 371 | is_c: self.intersects(Self::IS_C), 372 | is_transparent: self.intersects(Self::IS_TRANSPARENT), 373 | is_linear: self.intersects(Self::IS_LINEAR), 374 | } 375 | } 376 | } 377 | 378 | impl<'tcx> Stable<'tcx> for rustc_abi::IntegerType { 379 | type T = IntegerType; 380 | 381 | fn stable<'cx>( 382 | &self, 383 | tables: &mut Tables<'cx, BridgeTys>, 384 | cx: &CompilerCtxt<'cx, BridgeTys>, 385 | ) -> Self::T { 386 | match self { 387 | rustc_abi::IntegerType::Pointer(signed) => IntegerType::Pointer { is_signed: *signed }, 388 | rustc_abi::IntegerType::Fixed(integer, signed) => { 389 | IntegerType::Fixed { length: integer.stable(tables, cx), is_signed: *signed } 390 | } 391 | } 392 | } 393 | } 394 | 395 | impl<'tcx> Stable<'tcx> for rustc_abi::ReprOptions { 396 | type T = ReprOptions; 397 | 398 | fn stable<'cx>( 399 | &self, 400 | tables: &mut Tables<'cx, BridgeTys>, 401 | cx: &CompilerCtxt<'cx, BridgeTys>, 402 | ) -> Self::T { 403 | ReprOptions { 404 | int: self.int.map(|int| int.stable(tables, cx)), 405 | align: self.align.map(|align| align.stable(tables, cx)), 406 | pack: self.pack.map(|pack| pack.stable(tables, cx)), 407 | flags: self.flags.stable(tables, cx), 408 | } 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /rustc_public/src/abi.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug}; 2 | use std::num::NonZero; 3 | use std::ops::RangeInclusive; 4 | 5 | use serde::Serialize; 6 | 7 | use crate::compiler_interface::with; 8 | use crate::mir::FieldIdx; 9 | use crate::target::{MachineInfo, MachineSize as Size}; 10 | use crate::ty::{Align, Ty, VariantIdx}; 11 | use crate::{Error, Opaque, error}; 12 | 13 | /// A function ABI definition. 14 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 15 | pub struct FnAbi { 16 | /// The types of each argument. 17 | pub args: Vec, 18 | 19 | /// The expected return type. 20 | pub ret: ArgAbi, 21 | 22 | /// The count of non-variadic arguments. 23 | /// 24 | /// Should only be different from `args.len()` when a function is a C variadic function. 25 | pub fixed_count: u32, 26 | 27 | /// The ABI convention. 28 | pub conv: CallConvention, 29 | 30 | /// Whether this is a variadic C function, 31 | pub c_variadic: bool, 32 | } 33 | 34 | /// Information about the ABI of a function's argument, or return value. 35 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 36 | pub struct ArgAbi { 37 | pub ty: Ty, 38 | pub layout: Layout, 39 | pub mode: PassMode, 40 | } 41 | 42 | /// How a function argument should be passed in to the target function. 43 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 44 | pub enum PassMode { 45 | /// Ignore the argument. 46 | /// 47 | /// The argument is either uninhabited or a ZST. 48 | Ignore, 49 | /// Pass the argument directly. 50 | /// 51 | /// The argument has a layout abi of `Scalar` or `Vector`. 52 | Direct(Opaque), 53 | /// Pass a pair's elements directly in two arguments. 54 | /// 55 | /// The argument has a layout abi of `ScalarPair`. 56 | Pair(Opaque, Opaque), 57 | /// Pass the argument after casting it. 58 | Cast { pad_i32: bool, cast: Opaque }, 59 | /// Pass the argument indirectly via a hidden pointer. 60 | Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, 61 | } 62 | 63 | /// The layout of a type, alongside the type itself. 64 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] 65 | pub struct TyAndLayout { 66 | pub ty: Ty, 67 | pub layout: Layout, 68 | } 69 | 70 | /// The layout of a type in memory. 71 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 72 | pub struct LayoutShape { 73 | /// The fields location within the layout 74 | pub fields: FieldsShape, 75 | 76 | /// Encodes information about multi-variant layouts. 77 | /// Even with `Multiple` variants, a layout still has its own fields! Those are then 78 | /// shared between all variants. 79 | /// 80 | /// To access all fields of this layout, both `fields` and the fields of the active variant 81 | /// must be taken into account. 82 | pub variants: VariantsShape, 83 | 84 | /// The `abi` defines how this data is passed between functions. 85 | pub abi: ValueAbi, 86 | 87 | /// The ABI mandated alignment in bytes. 88 | pub abi_align: Align, 89 | 90 | /// The size of this layout in bytes. 91 | pub size: Size, 92 | } 93 | 94 | impl LayoutShape { 95 | /// Returns `true` if the layout corresponds to an unsized type. 96 | #[inline] 97 | pub fn is_unsized(&self) -> bool { 98 | self.abi.is_unsized() 99 | } 100 | 101 | #[inline] 102 | pub fn is_sized(&self) -> bool { 103 | !self.abi.is_unsized() 104 | } 105 | 106 | /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). 107 | pub fn is_1zst(&self) -> bool { 108 | self.is_sized() && self.size.bits() == 0 && self.abi_align == 1 109 | } 110 | } 111 | 112 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] 113 | pub struct Layout(usize); 114 | 115 | impl Layout { 116 | pub fn shape(self) -> LayoutShape { 117 | with(|cx| cx.layout_shape(self)) 118 | } 119 | } 120 | 121 | impl crate::IndexedVal for Layout { 122 | fn to_val(index: usize) -> Self { 123 | Layout(index) 124 | } 125 | fn to_index(&self) -> usize { 126 | self.0 127 | } 128 | } 129 | 130 | /// Describes how the fields of a type are shaped in memory. 131 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 132 | pub enum FieldsShape { 133 | /// Scalar primitives and `!`, which never have fields. 134 | Primitive, 135 | 136 | /// All fields start at no offset. The `usize` is the field count. 137 | Union(NonZero), 138 | 139 | /// Array/vector-like placement, with all fields of identical types. 140 | Array { stride: Size, count: u64 }, 141 | 142 | /// Struct-like placement, with precomputed offsets. 143 | /// 144 | /// Fields are guaranteed to not overlap, but note that gaps 145 | /// before, between and after all the fields are NOT always 146 | /// padding, and as such their contents may not be discarded. 147 | /// For example, enum variants leave a gap at the start, 148 | /// where the discriminant field in the enum layout goes. 149 | Arbitrary { 150 | /// Offsets for the first byte of each field, 151 | /// ordered to match the source definition order. 152 | /// I.e.: It follows the same order as [super::ty::VariantDef::fields()]. 153 | /// This vector does not go in increasing order. 154 | offsets: Vec, 155 | }, 156 | } 157 | 158 | impl FieldsShape { 159 | pub fn fields_by_offset_order(&self) -> Vec { 160 | match self { 161 | FieldsShape::Primitive => vec![], 162 | FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(), 163 | FieldsShape::Arbitrary { offsets, .. } => { 164 | let mut indices = (0..offsets.len()).collect::>(); 165 | indices.sort_by_key(|idx| offsets[*idx]); 166 | indices 167 | } 168 | } 169 | } 170 | 171 | pub fn count(&self) -> usize { 172 | match self { 173 | FieldsShape::Primitive => 0, 174 | FieldsShape::Union(count) => count.get(), 175 | FieldsShape::Array { count, .. } => *count as usize, 176 | FieldsShape::Arbitrary { offsets, .. } => offsets.len(), 177 | } 178 | } 179 | } 180 | 181 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 182 | pub enum VariantsShape { 183 | /// A type with no valid variants. Must be uninhabited. 184 | Empty, 185 | 186 | /// Single enum variants, structs/tuples, unions, and all non-ADTs. 187 | Single { index: VariantIdx }, 188 | 189 | /// Enum-likes with more than one inhabited variant: each variant comes with 190 | /// a *discriminant* (usually the same as the variant index but the user can 191 | /// assign explicit discriminant values). That discriminant is encoded 192 | /// as a *tag* on the machine. The layout of each variant is 193 | /// a struct, and they all have space reserved for the tag. 194 | /// For enums, the tag is the sole field of the layout. 195 | Multiple { 196 | tag: Scalar, 197 | tag_encoding: TagEncoding, 198 | tag_field: usize, 199 | variants: Vec, 200 | }, 201 | } 202 | 203 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 204 | pub enum TagEncoding { 205 | /// The tag directly stores the discriminant, but possibly with a smaller layout 206 | /// (so converting the tag to the discriminant can require sign extension). 207 | Direct, 208 | 209 | /// Niche (values invalid for a type) encoding the discriminant: 210 | /// Discriminant and variant index coincide. 211 | /// The variant `untagged_variant` contains a niche at an arbitrary 212 | /// offset (field `tag_field` of the enum), which for a variant with 213 | /// discriminant `d` is set to 214 | /// `(d - niche_variants.start).wrapping_add(niche_start)`. 215 | /// 216 | /// For example, `Option<(usize, &T)>` is represented such that 217 | /// `None` has a null pointer for the second tuple field, and 218 | /// `Some` is the identity function (with a non-null reference). 219 | Niche { 220 | untagged_variant: VariantIdx, 221 | niche_variants: RangeInclusive, 222 | niche_start: u128, 223 | }, 224 | } 225 | 226 | /// Describes how values of the type are passed by target ABIs, 227 | /// in terms of categories of C types there are ABI rules for. 228 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] 229 | pub enum ValueAbi { 230 | Scalar(Scalar), 231 | ScalarPair(Scalar, Scalar), 232 | Vector { 233 | element: Scalar, 234 | count: u64, 235 | }, 236 | Aggregate { 237 | /// If true, the size is exact, otherwise it's only a lower bound. 238 | sized: bool, 239 | }, 240 | } 241 | 242 | impl ValueAbi { 243 | /// Returns `true` if the layout corresponds to an unsized type. 244 | pub fn is_unsized(&self) -> bool { 245 | match *self { 246 | ValueAbi::Scalar(_) | ValueAbi::ScalarPair(..) | ValueAbi::Vector { .. } => false, 247 | ValueAbi::Aggregate { sized } => !sized, 248 | } 249 | } 250 | } 251 | 252 | /// Information about one scalar component of a Rust type. 253 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] 254 | pub enum Scalar { 255 | Initialized { 256 | /// The primitive type used to represent this value. 257 | value: Primitive, 258 | /// The range that represents valid values. 259 | /// The range must be valid for the `primitive` size. 260 | valid_range: WrappingRange, 261 | }, 262 | Union { 263 | /// Unions never have niches, so there is no `valid_range`. 264 | /// Even for unions, we need to use the correct registers for the kind of 265 | /// values inside the union, so we keep the `Primitive` type around. 266 | /// It is also used to compute the size of the scalar. 267 | value: Primitive, 268 | }, 269 | } 270 | 271 | impl Scalar { 272 | pub fn has_niche(&self, target: &MachineInfo) -> bool { 273 | match self { 274 | Scalar::Initialized { value, valid_range } => { 275 | !valid_range.is_full(value.size(target)).unwrap() 276 | } 277 | Scalar::Union { .. } => false, 278 | } 279 | } 280 | } 281 | 282 | /// Fundamental unit of memory access and layout. 283 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)] 284 | pub enum Primitive { 285 | /// The `bool` is the signedness of the `Integer` type. 286 | /// 287 | /// One would think we would not care about such details this low down, 288 | /// but some ABIs are described in terms of C types and ISAs where the 289 | /// integer arithmetic is done on {sign,zero}-extended registers, e.g. 290 | /// a negative integer passed by zero-extension will appear positive in 291 | /// the callee, and most operations on it will produce the wrong values. 292 | Int { 293 | length: IntegerLength, 294 | signed: bool, 295 | }, 296 | Float { 297 | length: FloatLength, 298 | }, 299 | Pointer(AddressSpace), 300 | } 301 | 302 | impl Primitive { 303 | pub fn size(self, target: &MachineInfo) -> Size { 304 | match self { 305 | Primitive::Int { length, .. } => Size::from_bits(length.bits()), 306 | Primitive::Float { length } => Size::from_bits(length.bits()), 307 | Primitive::Pointer(_) => target.pointer_width, 308 | } 309 | } 310 | } 311 | 312 | /// Enum representing the existing integer lengths. 313 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] 314 | pub enum IntegerLength { 315 | I8, 316 | I16, 317 | I32, 318 | I64, 319 | I128, 320 | } 321 | 322 | /// Enum representing the existing float lengths. 323 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] 324 | pub enum FloatLength { 325 | F16, 326 | F32, 327 | F64, 328 | F128, 329 | } 330 | 331 | impl IntegerLength { 332 | pub fn bits(self) -> usize { 333 | match self { 334 | IntegerLength::I8 => 8, 335 | IntegerLength::I16 => 16, 336 | IntegerLength::I32 => 32, 337 | IntegerLength::I64 => 64, 338 | IntegerLength::I128 => 128, 339 | } 340 | } 341 | } 342 | 343 | impl FloatLength { 344 | pub fn bits(self) -> usize { 345 | match self { 346 | FloatLength::F16 => 16, 347 | FloatLength::F32 => 32, 348 | FloatLength::F64 => 64, 349 | FloatLength::F128 => 128, 350 | } 351 | } 352 | } 353 | 354 | /// An identifier that specifies the address space that some operation 355 | /// should operate on. Special address spaces have an effect on code generation, 356 | /// depending on the target and the address spaces it implements. 357 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] 358 | pub struct AddressSpace(pub u32); 359 | 360 | impl AddressSpace { 361 | /// The default address space, corresponding to data space. 362 | pub const DATA: Self = AddressSpace(0); 363 | } 364 | 365 | /// Inclusive wrap-around range of valid values (bitwise representation), that is, if 366 | /// start > end, it represents `start..=MAX`, followed by `0..=end`. 367 | /// 368 | /// That is, for an i8 primitive, a range of `254..=2` means following 369 | /// sequence: 370 | /// 371 | /// 254 (-2), 255 (-1), 0, 1, 2 372 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] 373 | pub struct WrappingRange { 374 | pub start: u128, 375 | pub end: u128, 376 | } 377 | 378 | impl WrappingRange { 379 | /// Returns `true` if `size` completely fills the range. 380 | #[inline] 381 | pub fn is_full(&self, size: Size) -> Result { 382 | let Some(max_value) = size.unsigned_int_max() else { 383 | return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits())); 384 | }; 385 | if self.start <= max_value && self.end <= max_value { 386 | Ok(self.start == (self.end.wrapping_add(1) & max_value)) 387 | } else { 388 | Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits())) 389 | } 390 | } 391 | 392 | /// Returns `true` if `v` is contained in the range. 393 | #[inline(always)] 394 | pub fn contains(&self, v: u128) -> bool { 395 | if self.wraps_around() { 396 | self.start <= v || v <= self.end 397 | } else { 398 | self.start <= v && v <= self.end 399 | } 400 | } 401 | 402 | /// Returns `true` if the range wraps around. 403 | /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`. 404 | /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`. 405 | #[inline] 406 | pub fn wraps_around(&self) -> bool { 407 | self.start > self.end 408 | } 409 | } 410 | 411 | impl Debug for WrappingRange { 412 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 413 | if self.start > self.end { 414 | write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; 415 | } else { 416 | write!(fmt, "{}..={}", self.start, self.end)?; 417 | } 418 | Ok(()) 419 | } 420 | } 421 | 422 | /// General language calling conventions. 423 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] 424 | pub enum CallConvention { 425 | C, 426 | Rust, 427 | 428 | Cold, 429 | PreserveMost, 430 | PreserveAll, 431 | 432 | Custom, 433 | 434 | // Target-specific calling conventions. 435 | ArmAapcs, 436 | CCmseNonSecureCall, 437 | CCmseNonSecureEntry, 438 | 439 | Msp430Intr, 440 | 441 | PtxKernel, 442 | 443 | GpuKernel, 444 | 445 | X86Fastcall, 446 | X86Intr, 447 | X86Stdcall, 448 | X86ThisCall, 449 | X86VectorCall, 450 | 451 | X86_64SysV, 452 | X86_64Win64, 453 | 454 | AvrInterrupt, 455 | AvrNonBlockingInterrupt, 456 | 457 | RiscvInterrupt, 458 | } 459 | 460 | #[non_exhaustive] 461 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] 462 | pub struct ReprFlags { 463 | pub is_simd: bool, 464 | pub is_c: bool, 465 | pub is_transparent: bool, 466 | pub is_linear: bool, 467 | } 468 | 469 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] 470 | pub enum IntegerType { 471 | /// Pointer-sized integer type, i.e. `isize` and `usize`. 472 | Pointer { 473 | /// Signedness. e.g. `true` for `isize` 474 | is_signed: bool, 475 | }, 476 | /// Fixed-sized integer type, e.g. `i8`, `u32`, `i128`. 477 | Fixed { 478 | /// Length of this integer type. e.g. `IntegerLength::I8` for `u8`. 479 | length: IntegerLength, 480 | /// Signedness. e.g. `false` for `u8` 481 | is_signed: bool, 482 | }, 483 | } 484 | 485 | /// Representation options provided by the user 486 | #[non_exhaustive] 487 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] 488 | pub struct ReprOptions { 489 | pub int: Option, 490 | pub align: Option, 491 | pub pack: Option, 492 | pub flags: ReprFlags, 493 | } 494 | --------------------------------------------------------------------------------