Skip to content

Conversation

@joshlf
Copy link
Member

@joshlf joshlf commented Feb 3, 2026


Latest Update: v4 — Compare vs v3

📚 Full Patch History

Links show the diff between the row version and the column version.

Version v3 v2 v1 Base
v4 vs v3 vs v2 vs v1 vs Base
v3 vs v2 vs v1 vs Base
v2 vs v1 vs Base
v1 vs Base
⬇️ Download this PR

Branch

git fetch origin refs/heads/Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31 && git checkout -b pr-Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31 FETCH_HEAD

Checkout

git fetch origin refs/heads/Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31 && git checkout FETCH_HEAD

Cherry Pick

git fetch origin refs/heads/Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31 && git cherry-pick FETCH_HEAD

Pull

git pull origin refs/heads/Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31

Stacked PRs enabled by GHerrit.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @joshlf, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant architectural change by implementing a 'shadow crate' strategy to manage unsafe Rust code within the formal verification pipeline. The core idea is to transform the original Rust code into a 'safe' equivalent for analysis: unsafe functions designated as Lean models are replaced with runtime checks for their specified preconditions, while other unsafe operations are simply stripped of their unsafe keywords. This enables the verification tool to process Rust code that leverages unsafe by either formally modeling its behavior or by treating it as safe after transformation, thereby extending the scope of verifiable Rust code.

Highlights

  • Shadow Crate Introduction: A new 'shadow crate' mechanism has been implemented to transform Rust code, specifically handling unsafe constructs, for formal verification purposes.
  • Unsafe Function Transformation: Unsafe functions marked with @ lean model are now transformed into safe shims that include runtime checks for their preconditions, panicking if violated. Other unsafe fns and unsafe { ... } blocks have their unsafe markers removed, effectively treating them as safe.
  • Lean Code Generation Enhancements: The Lean code generation now injects OfNat instances to support numeric literals in specifications and differentiates between theorem and axiom declarations for Lean models based on the presence of explicit proofs.
  • Dependency Update: The syn crate dependency has been updated to include the visit-mut feature, which is crucial for the AST transformation logic in the shadow crate.
  • New Test Cases: Several new test cases have been added to validate the shadow crate's behavior, including scenarios for successful model verification and expected failures due to contract or precondition violations.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • tools/hermes/Cargo.toml
    • Added visit-mut feature to the syn dependency.
  • tools/hermes/src/lib.rs
    • Introduced a new public module shadow.
  • tools/hermes/src/parser.rs
    • Added an is_model: bool field to the ParsedFunction struct.
    • Modified SpecVisitor to detect @ lean model attributes and populate the is_model field.
    • Refined the trimming logic for spec_lines.
  • tools/hermes/src/pipeline.rs
    • Integrated a new 'Step 1: Creating Shadow Crate...' into the pipeline, utilizing crate::shadow::create_shadow_crate.
    • Updated run_charon to operate on the shadow crate's root and manifest path.
    • Removed redundant source_file logic before stitch_user_proofs.
    • Injected Lean OfNat instances for various integer types (U32, I32, Usize, Isize) to support numeric literals in specifications.
    • Modified generate_lean_file to handle is_model for proof body generation, allowing models without explicit proofs to be implicitly trusted.
    • Changed the Lean declaration type from theorem to axiom for models that lack explicit proofs.
  • tools/hermes/src/shadow.rs
    • New file: Implements the core logic for creating and sanitizing the shadow crate.
    • Includes create_shadow_crate for copying the original crate structure to a temporary directory (target/hermes_shadow).
    • Provides sanitize_crate and process_file functions to iterate and transform Rust files within the shadow crate.
    • Defines ShadowVisitor, a syn::visit_mut::VisitMut implementation that performs the unsafe transformations.
    • Implements parse_model_specs to extract is_model status and @ requires clauses from doc comments.
    • Contains extensive unit tests covering various unsafe transformation scenarios.
  • tools/hermes/tests/cases/failure/checked_add_sorry.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/failure/incorrect_proof.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/failure/shadow_contract_fail.rs
    • New file: Added a test case demonstrating a proof failure when a model's contract is violated.
  • tools/hermes/tests/cases/failure/shadow_prec_fail.rs
    • New file: Added a test case demonstrating a proof failure when a model's precondition is not met.
  • tools/hermes/tests/cases/failure/struct_spec.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/failure/syntax_error_in_spec.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/failure/unchecked_wrapper_sorry.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/failure/undefined_variable.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/success/generic_id.rs
    • Minor formatting adjustment (whitespace).
  • tools/hermes/tests/cases/success/shadow_model.rs
    • New file: Added a successful test case showcasing the shadow model functionality with a safe_div example.
  • tools/hermes/tests/cases/success/struct_method.rs
    • Minor formatting adjustment (whitespace).
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @joshlf, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a 'shadow crate' mechanism to Hermes for handling and verifying unsafe Rust code. It involves creating a modified copy of the original crate where unsafe code is either removed or replaced with safe shims based on specifications. This allows the verifier to reason about the code in a safer context. The PR also includes updates to the parser to recognize model specifications and injects OfNat instances to support numeric literals in specs.

Highlights

  • Cargo.toml Update: The syn dependency in tools/hermes/Cargo.toml has been updated to include the visit-mut feature.
  • Shadow Crate Implementation: A new module shadow has been added to tools/hermes/src/lib.rs, and a corresponding file tools/hermes/src/shadow.rs has been created to handle the creation and sanitization of a shadow crate for unsafe code verification.
  • Parser Modification: The ParsedFunction struct in tools/hermes/src/parser.rs now includes a boolean field is_model to distinguish between regular specs and model specs. The parsing logic has been updated to identify @ lean model annotations and set the is_model flag accordingly.
  • Pipeline Changes: The run_pipeline function in tools/hermes/src/pipeline.rs has been modified to create a shadow crate before running Charon. This involves copying the original crate, sanitizing it, and then running Charon on the shadow crate. The pipeline now also injects OfNat instances into the generated Lean file.
  • Test Cases: Several new test cases have been added to tools/hermes/tests/cases/failure/ and tools/hermes/tests/cases/success/ to test the shadow crate functionality, including cases for contract violations and precondition failures.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • tools/hermes/Cargo.toml
    • Added visit-mut feature to syn dependency.
  • tools/hermes/src/lib.rs
    • Added shadow module.
  • tools/hermes/src/parser.rs
    • Added is_model field to ParsedFunction struct.
    • Updated parsing logic to identify @ lean model annotations.
  • tools/hermes/src/pipeline.rs
    • Implemented shadow crate creation and sanitization.
    • Modified run_charon to use the shadow crate.
    • Injected OfNat instances into the generated Lean file.
  • tools/hermes/src/shadow.rs
    • Implemented create_shadow_crate function to create and sanitize a shadow crate.
    • Implemented ShadowVisitor struct to remove or replace unsafe code.
  • tools/hermes/tests/cases/failure/checked_add_sorry.rs
    • Minor formatting change: added a newline before fn main() {}.
  • tools/hermes/tests/cases/failure/incorrect_proof.rs
    • Minor formatting change: added a newline before [dependencies].
  • tools/hermes/tests/cases/failure/shadow_contract_fail.rs
    • Added new test case to verify contract violations in shadow crate.
  • tools/hermes/tests/cases/failure/shadow_prec_fail.rs
    • Added new test case to verify precondition failures in shadow crate.
  • tools/hermes/tests/cases/failure/struct_spec.rs
    • Minor formatting change: added a newline before [dependencies].
  • tools/hermes/tests/cases/failure/syntax_error_in_spec.rs
    • Minor formatting change: added a newline before [dependencies].
  • tools/hermes/tests/cases/failure/unchecked_wrapper_sorry.rs
    • Minor formatting change: added a newline before [dependencies] and simplified the safe_inc function.
  • tools/hermes/tests/cases/failure/undefined_variable.rs
    • Minor formatting change: added a newline before [dependencies].
  • tools/hermes/tests/cases/success/generic_id.rs
    • Minor formatting change: added a newline before fn main() {}.
  • tools/hermes/tests/cases/success/shadow_model.rs
    • Added new test case to verify shadow crate functionality with model specifications.
  • tools/hermes/tests/cases/success/struct_method.rs
    • Minor formatting change: added a newline before fn main() {}.
Activity
  • The PR introduces a shadow crate mechanism for handling unsafe Rust code.
  • The syn dependency is updated to include the visit-mut feature.
  • The ParsedFunction struct is modified to include a boolean field is_model.
  • The run_pipeline function is updated to create a shadow crate before running Charon.
  • New test cases are added to verify the shadow crate functionality.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-designed feature for handling unsafe code by creating a "shadow crate". In this shadow crate, unsafe functions are transformed into safe shims based on their contracts, or unsafe blocks are unwrapped, allowing for verification. The implementation is comprehensive, including parsing, AST transformation, and updates to the verification pipeline. I've found a few areas for improvement, including removing debug statements, enhancing parsing robustness, and addressing a potential bug in path handling within the pipeline. Overall, this is a strong contribution that greatly enhances the tool's capabilities.

if let Ok(rel) = path.strip_prefix(crate_root) {
Some(shadow_crate_root.join(rel))
} else {
Some(path.clone())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This else branch seems problematic. If manifest_path is outside crate_root, strip_prefix will fail, and shadow_manifest_path will be set to the original manifest_path. However, run_charon is executed inside the shadow_crate_root. This will likely cause charon to run on the original crate instead of the shadowed one, defeating the purpose of this new mechanism. This case should probably result in an error or be handled differently to ensure the shadowed manifest is always used.


for attr in attrs {
if let Some(doc) = parse_doc_attr(attr) {
println!("DEBUG: Doc attr: {:?}", doc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This println! and others in this function (lines 219, 224) appear to be for debugging. They should be removed before merging to keep the tool's output clean.

println!("DEBUG: Trimmed line: '{}'", trimmed);
// Check found marker e.g. "@ lean model"
if trimmed.starts_with('@') {
if trimmed.contains("lean model") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using contains("lean model") is less precise than strip_prefix and could lead to false positives (e.g., on a comment like ///@ lean model_v2). It's also inconsistent with parser.rs, which uses strip_prefix. For improved robustness and consistency, it would be better to use strip_prefix here as well.

Suggested change
if trimmed.contains("lean model") {
if trimmed.strip_prefix("@ lean model").is_some() {

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant new feature: the creation of a "shadow crate" to handle unsafe code during verification. The core idea is to generate a sanitized version of the user's crate where unsafe blocks are either removed or replaced by safe, model-based shims. This allows the verification toolchain to analyze the code in a safe context. The changes are well-contained within the new shadow module and its integration into the main pipeline. My review focuses on improving the robustness and clarity of the new shadow.rs implementation, addressing issues related to error handling, debug artifacts, and code maintainability.

Comment on lines +210 to +242
fn parse_model_specs(attrs: &[Attribute]) -> (bool, Vec<String>) {
let mut is_model = false;
let mut requires = Vec::new();

for attr in attrs {
if let Some(doc) = parse_doc_attr(attr) {
println!("DEBUG: Doc attr: {:?}", doc);
for line in doc.lines() {
let trimmed = line.trim();
println!("DEBUG: Trimmed line: '{}'", trimmed);
// Check found marker e.g. "@ lean model"
if trimmed.starts_with('@') {
if trimmed.contains("lean model") {
is_model = true;
println!("DEBUG: Found model marker!");
}

if let Some(rest) = trimmed.strip_prefix("@ requires") {
let content = rest.trim();
// Strip binder name if present (e.g. "h : x > 0" -> "x > 0")
let condition = if let Some((_, expr)) = content.split_once(':') {
expr.trim()
} else {
content
};
requires.push(condition.to_string());
}
}
}
}
}
(is_model, requires)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This function contains several println! statements (on lines 216, 219, and 224) that appear to be for debugging. These should be removed from production code to avoid printing unnecessary output to the console when the tool is used.

Comment on lines +145 to +146
let expr = syn::parse_str::<syn::Expr>(&combined_str)
.unwrap_or_else(|_| parse_quote!(true));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of unwrap_or_else(|_| parse_quote!(true)) silently ignores errors when parsing preconditions from @requires annotations. If a precondition is malformed, it will default to true, which could lead to unsound assumptions during verification (i.e., a contract violation check might be skipped). It would be safer to panic with a descriptive error if parsing fails, to alert the user to the malformed annotation.

Suggested change
let expr = syn::parse_str::<syn::Expr>(&combined_str)
.unwrap_or_else(|_| parse_quote!(true));
let expr = syn::parse_str::<syn::Expr>(&combined_str)
.expect("Malformed @requires precondition");

Comment on lines +178 to +194
fn visit_expr_mut(&mut self, node: &mut Expr) {
if let Expr::Unsafe(expr_unsafe) = node {
// Transform `unsafe { ... }` into `{ ... }`
// We preserve attributes and the inner block
let new_expr = Expr::Block(ExprBlock {
attrs: expr_unsafe.attrs.clone(),
label: None,
block: expr_unsafe.block.clone(),
});
*node = new_expr;

// Recurse into the new block (in case there are nested unsafe blocks)
self.visit_expr_mut(node);
} else {
syn::visit_mut::visit_expr_mut(self, node);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The implementation of visit_expr_mut is a bit confusing due to the direct recursive call self.visit_expr_mut(node). While correct, it relies on a subtle interaction with the else branch to traverse into the newly created block. A post-order traversal approach would be more conventional and easier to understand. By first visiting child nodes and then transforming the current node, the logic becomes more straightforward and maintainable.

    fn visit_expr_mut(&mut self, node: &mut Expr) {
        // First, traverse deeper into the AST to handle nested expressions.
        syn::visit_mut::visit_expr_mut(self, node);

        // After children have been processed, transform the current node if it's `unsafe`.
        if let Expr::Unsafe(expr_unsafe) = node {
            // Transform `unsafe { ... }` into `{ ... }`
            *node = Expr::Block(ExprBlock {
                attrs: expr_unsafe.attrs.clone(),
                label: None,
                block: expr_unsafe.block.clone(),
            });
        }
    }

println!("DEBUG: Trimmed line: '{}'", trimmed);
// Check found marker e.g. "@ lean model"
if trimmed.starts_with('@') {
if trimmed.contains("lean model") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check trimmed.contains("lean model") is too broad and could lead to false positives. For example, it would match a comment like ///@ this is not a lean model. It would be more robust to use strip_prefix to ensure the annotation is at the beginning of the string, which would also make it consistent with how @ lean spec is handled in parser.rs.

                    if trimmed.strip_prefix("@ lean model").is_some() {

@codecov-commenter
Copy link

codecov-commenter commented Feb 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.02%. Comparing base (a24d41d) to head (9a2da12).

Additional details and impacted files
@@                            Coverage Diff                             @@
##           G443fc7f8c3623131b48be56a7a16935a9fe8f234    #2972   +/-   ##
==========================================================================
  Coverage                                      92.02%   92.02%           
==========================================================================
  Files                                             19       19           
  Lines                                           6029     6029           
==========================================================================
  Hits                                            5548     5548           
  Misses                                           481      481           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@joshlf joshlf force-pushed the Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31 branch from 6d9291e to 8f7e2d6 Compare February 3, 2026 02:57
This was referenced Feb 3, 2026
gherrit-pr-id: Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31
@joshlf joshlf force-pushed the Gb85ad5e55a47c49ab2c049a2ec5972fc6d082e31 branch from 8f7e2d6 to 9a2da12 Compare February 4, 2026 05:35
@joshlf joshlf force-pushed the G443fc7f8c3623131b48be56a7a16935a9fe8f234 branch from 5ac313d to a24d41d Compare February 4, 2026 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants