Skip to content

Implement Linearization for dependency resolution#279

Merged
KyrylR merged 1 commit intoBlockstreamResearch:dev/importsfrom
LesterEvSe:feature/c3-linearization
Apr 9, 2026
Merged

Implement Linearization for dependency resolution#279
KyrylR merged 1 commit intoBlockstreamResearch:dev/importsfrom
LesterEvSe:feature/c3-linearization

Conversation

@LesterEvSe
Copy link
Copy Markdown
Collaborator

@LesterEvSe LesterEvSe commented Apr 8, 2026

This PR introduces the Linearization algorithm to manage module dependencies. It handles complex dependency topologies, detects cycles, and ensures strict, deterministic load ordering.

@LesterEvSe LesterEvSe requested a review from KyrylR April 8, 2026 14:25
@LesterEvSe LesterEvSe requested a review from delta1 as a code owner April 8, 2026 14:25
@LesterEvSe LesterEvSe added the enhancement New feature or request label Apr 8, 2026
@LesterEvSe LesterEvSe force-pushed the feature/c3-linearization branch from e4183d9 to a807f7f Compare April 8, 2026 14:37
@KyrylR
Copy link
Copy Markdown
Collaborator

KyrylR commented Apr 8, 2026

Is this up to date: #238?

@LesterEvSe
Copy link
Copy Markdown
Collaborator Author

Is this up to date: #238?

Yes, but it is much more optimized and has been refactored extensively

Comment on lines +53 to +63
let mut seqs: Vec<Vec<usize>> = Vec::with_capacity(parents.len() + 1);

for &parent in parents {
seqs.push(self.linearize_rec(parent, memo, visiting)?);
}

let mut result = vec![module];
let merged = c3_merge(seqs).map_err(|conflicts| C3Error::InconsistentLinearization {
module: self.modules[module].source.str_name(),
conflicts: self.format_conflict_names(conflicts),
})?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

From what I understand the C3 should merge each parent linearization plus the raw parents list, otherwise the direct parent order is never part of the constraints. With the current code, parents [A, B] and linearizations [A] / [B, A] produce [B, A], even though that order contradicts the explicit parent list.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

    #[test]
    fn test_c3_rejects_conflicting_direct_parent_order() {
        // main declares parents in the order [A, B].
        // But B itself depends on A, so B's linearization is [B, A].
        //
        // True C3 must merge:
        //   [A]
        //   [B, A]
        //   [A, B]   <- the raw parent list
        //
        // That merge has no valid head, so this must be rejected as inconsistent.
        let (graph, _, _dir) = setup_graph(vec![
            ("main.simf", "use lib::A::foo; use lib::B::bar;"),
            ("libs/lib/A.simf", ""),
            ("libs/lib/B.simf", "use lib::A::foo;"),
        ]);

        let order = graph.c3_linearize();

        assert!(
            matches!(order, Err(C3Error::InconsistentLinearization { .. })),
            "expected inconsistent linearization, got {order:?}"
        );
    }

Copy link
Copy Markdown
Collaborator Author

@LesterEvSe LesterEvSe Apr 9, 2026

Choose a reason for hiding this comment

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

Fixed and added a new test. I accidentally missed adding the parents' order to the sequences when I refactored the code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Update: I was mistaken in my previous comment. The test_c3_allows_valid_parent_chain() proves that we cannot enforce strict C3 parent order without breaking valid module imports. I have retained the 'Dependency-Driven' C3 approach and added a top-level docstring to formally document this architectural decision

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Update 2: Taking this logic a step further—since we explicitly do not want to enforce strict local precedence for module imports, carrying the C3 is actually unnecessary. A standard post-order DFS provides this as well, but without overhead

]);

let order = graph.c3_linearize();
matches!(order, Err(C3Error::CycleDetected(_)));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

matches! just returns a boolean... the result is discarded here

@LesterEvSe LesterEvSe force-pushed the feature/c3-linearization branch from a807f7f to 51b9ceb Compare April 9, 2026 08:30
@LesterEvSe LesterEvSe requested a review from KyrylR April 9, 2026 08:37
@KyrylR
Copy link
Copy Markdown
Collaborator

KyrylR commented Apr 9, 2026

    #[test]
    fn test_c3_allows_valid_parent_chain() {
        // main imports A, then B; B itself imports A.
        let (graph, ids, _dir) = setup_graph(vec![
            ("main.simf", "use lib::A::foo; use lib::B::bar;"),
            ("libs/lib/A.simf", ""),
            ("libs/lib/B.simf", "use lib::A::foo;"),
        ]);

        let order = graph
            .c3_linearize()
            .expect("valid dependency DAG should linearize successfully");

        let main_id = ids["main"];
        let a_id = ids["A"];
        let b_id = ids["B"];

        assert_eq!(order, vec![main_id, b_id, a_id]);
    }

Now the valid import sequence is rejected

@KyrylR
Copy link
Copy Markdown
Collaborator

KyrylR commented Apr 9, 2026

Also, is it expected that the order is top-down?


use crate::driver::DependencyGraph;

impl DependencyGraph {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Give a link in the rust doc to the parent structure pls

@LesterEvSe LesterEvSe force-pushed the feature/c3-linearization branch 2 times, most recently from 927cfb4 to a49865f Compare April 9, 2026 12:16
@LesterEvSe
Copy link
Copy Markdown
Collaborator Author

LesterEvSe commented Apr 9, 2026

Also, is it expected that the order is top-down?

I have decided to move order.reverse() directly to the c3_linearize() function. So now it is bottom-up at this stage

@LesterEvSe LesterEvSe requested a review from KyrylR April 9, 2026 12:26
@LesterEvSe LesterEvSe force-pushed the feature/c3-linearization branch from a49865f to 105ea6f Compare April 9, 2026 13:57
@LesterEvSe LesterEvSe force-pushed the feature/c3-linearization branch from 105ea6f to 9ca524c Compare April 9, 2026 14:05
@LesterEvSe LesterEvSe changed the title Implement C3 Linearization for dependency resolution Implement Linearization for dependency resolution Apr 9, 2026
Copy link
Copy Markdown
Collaborator

@KyrylR KyrylR left a comment

Choose a reason for hiding this comment

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

ACK 9ca524c

@KyrylR KyrylR merged commit 9ca524c into BlockstreamResearch:dev/imports Apr 9, 2026
11 checks passed
@KyrylR KyrylR mentioned this pull request Apr 9, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants