Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 96 additions & 2 deletions kani-compiler/src/kani_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ use crate::kani_middle::check_crate_items;
use crate::kani_queries::QUERY_DB;
use crate::session::init_session;
use clap::Parser;
use rustc_ast::{ast, attr};
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_driver::{Callbacks, Compilation, run_compiler};
use rustc_interface::Config;
use rustc_interface::interface::Compiler;
use rustc_middle::ty::TyCtxt;
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str;
use rustc_parse::parser::ForceCollect;
use rustc_public::rustc_internal;
use rustc_session::config::ErrorOutputType;
use rustc_span::{FileName, sym};
use tracing::debug;

/// Run the Kani flavour of the compiler.
Expand Down Expand Up @@ -84,12 +90,17 @@ fn backend(args: Arguments) -> Box<CodegenBackend> {
///
/// It is responsible for initializing the query database, as well as controlling the compiler
/// state machine.
struct KaniCompiler {}
struct KaniCompiler {
/// Whether we are currently building the standard library. When set, Kani's
/// macro overrides are not injected (the macros are defined in the standard
/// library being built, and `kani` is not available).
build_std: bool,
}

impl KaniCompiler {
/// Create a new [KaniCompiler] instance.
pub fn new() -> KaniCompiler {
KaniCompiler {}
KaniCompiler { build_std: false }
}

/// Compile the current crate with the given arguments.
Expand All @@ -112,6 +123,10 @@ impl Callbacks for KaniCompiler {
let args = Arguments::parse_from(args);
init_session(&args, matches!(config.opts.error_format, ErrorOutputType::Json { .. }));

// Remember whether we are building the standard library so that
// `after_crate_root_parsing` can decide whether to inject Kani's macro overrides.
self.build_std = args.build_std;

// Capture args in the closure so they're available when the backend is created
// (potentially on a different thread).
config.make_codegen_backend = Some(Box::new({
Expand All @@ -120,6 +135,47 @@ impl Callbacks for KaniCompiler {
}));
}

/// Inject Kani's macro overrides into every (non-`#![no_std]`) crate we compile.
///
/// Kani overrides several `core`/`std` macros (e.g. `assert_eq!` without a
/// `Debug` bound, `panic!` with Kani-specific message handling, reachability
/// instrumentation for `assert!`). These overrides are intentionally kept out of
/// the standard-library prelude: since they are `core`-prelude macros, placing
/// them there makes `#![no_std]` dependencies that import `std`'s prelude
/// explicitly (`extern crate std; use std::prelude::v1::*;`, e.g. `lazy_static`)
/// ambiguous against the auto-injected `core` prelude, which is a hard error
/// (rust-lang/rust E0659).
///
/// Instead we bring the overrides into scope crate-wide by injecting a
/// `#[macro_use]`d re-import of the standard library. This is applied to every
/// crate Kani codegens — dependencies included, so that their assertions get the
/// same instrumentation and messages as the crate under verification — except:
/// - when building the standard library itself (the macros live there and `kani`
/// is not available), and
/// - `#![no_std]`/`#![no_core]` crates, which must not gain an `extern crate std`
/// (and which is exactly the case that would otherwise hit E0659).
///
/// A `#![no_std]` dependency therefore keeps the regular `core` macros. This is
/// sound: Kani still intercepts the panics they lower to during codegen.
fn after_crate_root_parsing(
&mut self,
compiler: &Compiler,
krate: &mut ast::Crate,
) -> Compilation {
if !self.build_std
&& !attr::contains_name(&krate.attrs, sym::no_std)
&& !attr::contains_name(&krate.attrs, sym::no_core)
// Only inject when an external `std` is available to import. The
// `verify-std` flow builds `std` itself (no `--extern std`), so
// injecting `extern crate std` there would pull in a *second* `std`
// (and `core`), causing duplicate-lang-item errors (E0152).
&& compiler.sess.opts.externs.get("std").is_some()
{
inject_kani_macro_overrides(compiler, krate);
}
Compilation::Continue
}

/// After analysis, we check the crate items for Kani API misuse or configuration issues.
fn after_analysis(
&mut self,
Expand All @@ -134,3 +190,41 @@ impl Callbacks for KaniCompiler {
Compilation::Continue
}
}

/// Inject `#[macro_use] extern crate std as _kani_std_macros;` at the crate root so
/// that Kani's `#[macro_export]`ed overrides (`assert!`, `assert_eq!`, `panic!`, …)
/// shadow the corresponding `std`/`core` prelude macros throughout the crate,
/// including nested modules.
///
/// A `#[macro_use]` extern crate places the macros in the higher-priority
/// "`macro_use` prelude" scope, so unlike a glob `use` (which would conflict with the
/// standard prelude, rust-lang/rust E0659) this shadows the prelude macros without
/// ambiguity. The crate is aliased (`as _kani_std_macros`) to avoid clashing with the
/// compiler-injected `extern crate std`.
fn inject_kani_macro_overrides(compiler: &Compiler, krate: &mut ast::Crate) {
let psess = &compiler.sess.psess;
let source = "#[allow(unused_extern_crates, unused_imports)]\n\
#[macro_use]\n\
extern crate std as _kani_std_macros;\n"
.to_string();
let filename = FileName::Custom("kani_macro_overrides".to_string());
let mut parser = match new_parser_from_source_str(psess, filename, source, StripTokens::Nothing)
{
Ok(parser) => parser,
Err(errors) => {
// This is an internal, statically-known source string, so a parse error
// indicates a Kani bug rather than a user error.
for error in errors {
error.emit();
}
return;
}
};
match parser.parse_item(ForceCollect::No) {
Ok(Some(item)) => krate.items.insert(0, item),
Ok(None) => unreachable!("failed to parse Kani's macro-override injection"),
Err(error) => {
error.emit();
}
}
}
1 change: 1 addition & 0 deletions kani-compiler/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ extern crate rustc_interface;
extern crate rustc_metadata;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
extern crate rustc_parse;
extern crate rustc_public;
extern crate rustc_public_bridge;
extern crate rustc_session;
Expand Down
44 changes: 44 additions & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,50 @@ pub use std::*;
// Override process calls with stubs.
pub mod process;

// The standard prelude explicitly re-exports the built-in macros
// (rust-lang/rust "Explicitly export core and std macros"), rather than
// injecting them via `#[macro_use] extern crate std`. Because `pub use std::*`
// above also re-exports `std::prelude`, user code would otherwise pick up the
// *original* `println!`/`assert!`/... from the std prelude and ignore the
// overrides defined below. Provide our own `prelude` (which shadows the glob
// re-export) that re-exports Kani's versions of the overridden macros. Explicit
// `use` imports take precedence over the `pub use std::prelude::...::*` globs,
// so only the overridden macros are replaced; everything else comes from std.
#[cfg(not(feature = "concrete_playback"))]
pub mod prelude {
// The standard prelude is kept free of Kani's `assert`/`debug_assert`/
// `unreachable`/`panic` overrides: those are core-prelude macros, and putting
// Kani's versions here makes `#![no_std]` dependencies that import this prelude
// explicitly (`extern crate std; use std::prelude::v1::*;`, e.g. lazy_static)
// ambiguous against the injected core prelude (E0659). Instead, kani-compiler
// injects those overrides only into the crate under verification (see
// kani_compiler's macro-override injection). The print family is defined in this
// crate (std) with no core-prelude counterpart, so overriding it here is safe and
// applies everywhere (dependencies included), which is important so that a
// dependency's `println!` does not run real formatting/IO during verification.
pub mod v1 {
pub use crate::{eprint, eprintln, print, println};
pub use std::prelude::v1::*;
}
pub mod rust_2015 {
pub use super::v1::*;
}
pub mod rust_2018 {
pub use super::v1::*;
}
pub mod rust_2021 {
pub use super::v1::*;
pub use core::prelude::rust_2021::*;
// Both globs bring `panic` (std's vs core's); prefer std's, matching std's own prelude.
pub use super::v1::panic;
}
pub mod rust_2024 {
pub use super::v1::panic;
pub use super::v1::*;
pub use core::prelude::rust_2024::*;
}
}

/// This assert macro calls kani's assert function passing it down the condition
/// as well as a message that will be used when reporting the assertion result.
///
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT

[toolchain]
channel = "nightly-2026-01-13"
channel = "nightly-2026-01-14"
components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"]
Loading