Skip to content

Conversation

@clementblaudeau
Copy link
Contributor

@clementblaudeau clementblaudeau commented Nov 24, 2025

Overview

This PR uses the module mechanism of the rust-engine to gather definitions in Lean namespaces, shortening their names. To do so, a refactor of global ids have been made, adding a proper FreshModule case. The logic for rendering those is not done yet (see #1779), as it will only be required by F*-like bundling in the rust engine (which might only come when porting the F* backend).

Example

  • Rust
// Simple trait
mod basic {
    trait T1 {
        fn f1(&self) -> usize;
        fn f2(&self, other: &Self) -> usize;
    }

    // Simple Impl
    struct S;

    impl T1 for S {
        fn f1(&self) -> usize {
            42
        }

        fn f2(&self, other: &Self) -> usize {
            43
        }
    }

    // Simple ImplExpr
    fn f<T: T1>(x: T) -> usize {
        x.f1() + x.f2(&x)
    }
}
  • Lean (without namespacing)
class Playground.Basic.T1 (Self : Type) where
  f1 : (Self -> RustM usize)
  f2 : (Self -> Self -> RustM usize)

structure Playground.Basic.S where


instance Playground.Basic.Impl : Playground.Basic.T1 Playground.Basic.S where
  f1 (self : Playground.Basic.S) := do (pure (42 : usize))
  f2 (self : Playground.Basic.S) (other : Playground.Basic.S) := do
    (pure (43 : usize))

def Playground.Basic.f
  (T : Type) [(Playground.Basic.T1 T)] (x : T)
  : RustM usize
  := do
  ((← (Playground.Basic.T1.f1 x)) +? (← (Playground.Basic.T1.f2 x x)))
  • Lean with namespacing
namespace Playground.Basic

class T1 (Self : Type) where
  f1 : (Self -> RustM usize)
  f2 : (Self -> Self -> RustM usize)

structure S where
  -- no fields

instance Impl : T1 S where
  f1 (self : S) := do (pure (42 : usize))
  f2 (self : S) (other : S) := do (pure (43 : usize))

def f (T : Type) [(T1 T)] (x : T) : RustM usize := do
  ((← (T1.f1 x)) +? (← (T1.f2 x x)))

end Playground.Basic

@clementblaudeau clementblaudeau changed the base branch from main to lean-default-methods November 24, 2025 11:51
* Context: In preparation for a namespace mechanism in the Lean backend, we
needed a way to compare items identifiers with the identifier of the current
module (to strip prefixes). However, hax can introduce
[fresh_modules] (typically, for bundles), and they did not have a normal
identifier: as they were not present in the rust code, they do not get an
[ExplicitDefId] and only exist as metadata (the [moved] field of
[ConcreteId]). To be able to handle the identifier of normal rust modules as
well as fresh modules in a unified way, this commit adds a new case in the
[GlobalId] enum.

This commit also contains some refactoring.

* refactor: remove conversion function from [ConcreteId] to [GlobalId]
* feat: add a memoized conversion function from [TupleId] to [ConcreteId]
- Add a comment for empty structures and inductives
- Do not print empty modules
- Capitalize the output-file name
@clementblaudeau clementblaudeau marked this pull request as ready for review November 24, 2025 13:22
@clementblaudeau clementblaudeau requested a review from a team as a code owner November 24, 2025 13:22
@clementblaudeau clementblaudeau requested review from W95Psp and removed request for a team November 24, 2025 13:22
Copy link
Member

@W95Psp W95Psp left a comment

Choose a reason for hiding this comment

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

Thanks, that generally looks good to me, though I have a few remarks :)

kind: hax_types::diagnostics::Kind::Unimplemented {
issue_id: Some(1779),
details: Some(
"Unsupported fresh modules, which should only come from the fstar-phase for bundling"
Copy link
Member

Choose a reason for hiding this comment

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

Maybe change to "Fresh modules are not implemented yet in the Rust engine" or something in those lines?
See my comment in #1779, it's not so much F* specific

Comment on lines +490 to +506
#[allow(unused)]
/// Prints identifiers for debugging/development. Should not be used in production code.
trait DebugString {
/// Debug printing of identifiers, for testing purposes only.
/// Prints path in a Rust-like way, as a `::` separated dismabiguated path.
fn to_debug_string(&self) -> String;
}

impl DebugString for TupleId {
fn to_debug_string(&self) -> String {
match self {
TupleId::Type { length } => format!("Tuple_type({length})"),
TupleId::Constructor { length } => format!("Tuple_constructor({length})"),
TupleId::Field { length, field } => format!("Tuple_projector({length}, {field})"),
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Let's implement https://doc.rust-lang.org/std/fmt/trait.Debug.html instead of creating a new trait

Comment on lines +213 to +217
// Removing unprintable items
let items: Vec<Item> = items
.into_iter()
.filter(LeanPrinter::printable_item)
.collect();
Copy link
Member

Choose a reason for hiding this comment

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

This should be a phase, no?

.collect()
}

fn apply(&self, mut items: Vec<Item>) -> Vec<File> {
Copy link
Member

Choose a reason for hiding this comment

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

We should pull that out of the backend trait: this method should not be overridable IMO 🤔

/// This runs all of the backend's [`Backend::phases`], groups the items into
/// modules via [`Backend::items_to_module`], and then uses the backend's printer
/// to generate source files with paths determined by [`Backend::module_path`].
pub fn apply_backend<B: Backend + 'static>(backend: B, mut items: Vec<Item>) -> Vec<File> {
Copy link
Member

Choose a reason for hiding this comment

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

Ah, that was not part of the trait?

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense, this should be the same logic for every backend

derive_generic_visitor = "0.3.0"
pastey = "0.1.0"
camino = "1.1.11"
capitalize = "0.3.4"
Copy link
Member

Choose a reason for hiding this comment

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

Let's not bring an extra dependency for such a small utility function, just add it to as an helper somewhere (e.g. in a helper.rs file)

@W95Psp W95Psp self-requested a review November 24, 2025 13:54
Copy link
Member

@W95Psp W95Psp left a comment

Choose a reason for hiding this comment

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

(sorry, I meant request changes, not approving 😓)

Base automatically changed from lean-default-methods to main November 27, 2025 18:17
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