From ea7fafe41fe51757b2a7e236c15b43183ab61bf3 Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:17:49 +0200 Subject: [PATCH] fix: Type canonicalization now canonicalizes the base path of path-based types too --- Cargo.lock | 1 + .../analyses/application_state/mod.rs | 3 + .../analyses/call_graph/application_state.rs | 3 + .../borrow_checker/ordered_call_graph.rs | 10 ++- .../compiler/analyses/call_graph/codegen.rs | 16 +++- .../analyses/call_graph/core_graph.rs | 12 ++- .../analyses/call_graph/dependency_graph.rs | 3 + .../analyses/call_graph/request_scoped.rs | 8 ++ .../compiler/analyses/components/db/mod.rs | 31 +++++-- .../src/compiler/analyses/constructibles.rs | 74 ++++++++++++---- .../src/compiler/analyses/error_handlers.rs | 54 ++++++++---- .../analyses/processing_pipeline/codegen.rs | 87 ++++++++++++++----- .../analyses/processing_pipeline/pipeline.rs | 47 ++++++---- compiler/pavexc/src/compiler/app.rs | 3 + compiler/pavexc/src/compiler/codegen/mod.rs | 6 +- .../pavexc/src/compiler/codegen/router.rs | 38 +++++--- compiler/pavexc/src/compiler/codegen/state.rs | 3 + compiler/pavexc/src/compiler/codegen_utils.rs | 18 ++-- .../src/compiler/component/constructor.rs | 8 +- .../rustdoc_ir/src/canonical_path_resolver.rs | 25 ++++++ rustdoc/rustdoc_ir/src/lib.rs | 2 + rustdoc/rustdoc_ir/src/type_.rs | 43 +++++++-- rustdoc/rustdoc_processor/Cargo.toml | 1 + rustdoc/rustdoc_processor/src/collection.rs | 8 ++ rustdoc/rustdoc_resolver/src/resolve_type.rs | 4 +- 25 files changed, 398 insertions(+), 110 deletions(-) create mode 100644 rustdoc/rustdoc_ir/src/canonical_path_resolver.rs diff --git a/Cargo.lock b/Cargo.lock index ce834b8eb..579252065 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4287,6 +4287,7 @@ dependencies = [ "rusqlite", "rustc-hash", "rustdoc_ext", + "rustdoc_ir", "rustdoc_types", "semver", "serde", diff --git a/compiler/pavexc/src/compiler/analyses/application_state/mod.rs b/compiler/pavexc/src/compiler/analyses/application_state/mod.rs index 287be8435..d07202983 100644 --- a/compiler/pavexc/src/compiler/analyses/application_state/mod.rs +++ b/compiler/pavexc/src/compiler/analyses/application_state/mod.rs @@ -57,6 +57,7 @@ impl ApplicationState { framework_item_db, constructibles_db, component_db, + krate_collection, ); runtime_singletons_are_thread_safe( &type2id, @@ -331,6 +332,7 @@ fn extract_runtime_singletons<'a>( framework_item_db: &FrameworkItemDb, constructibles_db: &ConstructibleDb, component_db: &ComponentDb, + krate_collection: &CrateCollection, ) -> IndexSet<(Type, ComponentId)> { let mut type2id = IndexSet::new(); for handler_pipeline in handler_pipelines { @@ -363,6 +365,7 @@ fn extract_runtime_singletons<'a>( root_component_scope_id, required_input, component_db.scope_graph(), + krate_collection, ) { let lifecycle = component_db.lifecycle(component_id); #[cfg(debug_assertions)] diff --git a/compiler/pavexc/src/compiler/analyses/call_graph/application_state.rs b/compiler/pavexc/src/compiler/analyses/call_graph/application_state.rs index 9f769060d..cbc59254c 100644 --- a/compiler/pavexc/src/compiler/analyses/call_graph/application_state.rs +++ b/compiler/pavexc/src/compiler/analyses/call_graph/application_state.rs @@ -62,6 +62,7 @@ pub(crate) fn application_state_call_graph( CloningPolicy::NeverClone, computation_db, framework_item_db, + krate_collection, None, ) .unwrap(); @@ -77,6 +78,7 @@ pub(crate) fn application_state_call_graph( computation_db, component_db, constructible_db, + krate_collection, lifecycle2invocations, diagnostics, ) @@ -296,6 +298,7 @@ pub(crate) fn application_state_call_graph( computation_db, component_db, constructible_db, + krate_collection, lifecycle2invocations, diagnostics, ) else { diff --git a/compiler/pavexc/src/compiler/analyses/call_graph/borrow_checker/ordered_call_graph.rs b/compiler/pavexc/src/compiler/analyses/call_graph/borrow_checker/ordered_call_graph.rs index 5756e3f0d..178fb3b9d 100644 --- a/compiler/pavexc/src/compiler/analyses/call_graph/borrow_checker/ordered_call_graph.rs +++ b/compiler/pavexc/src/compiler/analyses/call_graph/borrow_checker/ordered_call_graph.rs @@ -12,6 +12,7 @@ use crate::compiler::analyses::components::{ComponentDb, ComponentId}; use crate::compiler::analyses::computations::ComputationDb; use crate::compiler::computation::Computation; use crate::language::Type; +use crate::rustdoc::CrateCollection; /// A [`CallGraph`] with nodes globally ordered according to their node index. /// Walking the graph according to the specified ordering guarantees that the generated code will @@ -43,11 +44,18 @@ impl OrderedCallGraph { package_id2name: &BiHashMap, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, ) -> Result { if tracing::event_enabled!(tracing::Level::TRACE) { self.print_debug_dot("Application state", component_db, computation_db); } - codegen_callable_closure(self, package_id2name, component_db, computation_db) + codegen_callable_closure( + self, + package_id2name, + component_db, + computation_db, + krate_collection, + ) } /// Return the set of types that must be provided as input to (recursively) build the handler's diff --git a/compiler/pavexc/src/compiler/analyses/call_graph/codegen.rs b/compiler/pavexc/src/compiler/analyses/call_graph/codegen.rs index f67a7f331..aa054d960 100644 --- a/compiler/pavexc/src/compiler/analyses/call_graph/codegen.rs +++ b/compiler/pavexc/src/compiler/analyses/call_graph/codegen.rs @@ -25,6 +25,7 @@ use crate::compiler::codegen_utils::{Fragment, VariableNameGenerator}; use crate::compiler::component::Constructor; use crate::compiler::computation::{Computation, MatchResultVariant}; use crate::language::{CanonicalType, Type}; +use crate::rustdoc::CrateCollection; /// Generate the dependency closure of the [`OrderedCallGraph`]'s root callable. /// @@ -35,6 +36,7 @@ pub(crate) fn codegen_callable_closure( package_id2name: &BiHashMap, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, ) -> Result { let input_parameter_types = call_graph.required_input_types(); let mut variable_generator = VariableNameGenerator::new(); @@ -43,7 +45,7 @@ pub(crate) fn codegen_callable_closure( .iter() .map(|type_| { let parameter_name = variable_generator.generate(); - (type_.canonicalize(), parameter_name) + (type_.canonicalize(krate_collection), parameter_name) }) .collect(); let body = codegen_callable_closure_body( @@ -53,11 +55,12 @@ pub(crate) fn codegen_callable_closure( component_db, computation_db, &mut variable_generator, + krate_collection, )?; let function = { let inputs = input_parameter_types.into_iter().map(|mut type_| { - let variable_name = ¶meter_bindings[&type_.canonicalize()]; + let variable_name = ¶meter_bindings[&type_.canonicalize(krate_collection)]; // We can set all the non-'static lifetimes to implied (i.e. '_) in function signatures. let original2renamed = type_ .named_lifetime_parameters() @@ -209,6 +212,7 @@ fn codegen_callable_closure_body( component_db: &ComponentDb, computation_db: &ComputationDb, variable_name_generator: &mut VariableNameGenerator, + krate_collection: &CrateCollection, ) -> Result { let mut at_most_once_constructor_blocks = IndexMap::::new(); let mut blocks = BTreeMap::::new(); @@ -224,6 +228,7 @@ fn codegen_callable_closure_body( component_db, computation_db, variable_name_generator, + krate_collection, &mut at_most_once_constructor_blocks, &mut blocks, &mut dfs, @@ -272,6 +277,7 @@ fn _codegen_callable_closure_body( component_db: &ComponentDb, computation_db: &ComputationDb, variable_name_generator: &mut VariableNameGenerator, + krate_collection: &CrateCollection, at_most_once_constructor_blocks: &mut IndexMap, blocks: &mut BTreeMap, dfs: &mut BasicBlockVisitor, @@ -310,6 +316,7 @@ fn _codegen_callable_closure_body( blocks, variable_name_generator, package_id2name, + krate_collection, )?; // This is the last node! // We don't need to assign its value to a variable. @@ -348,7 +355,9 @@ fn _codegen_callable_closure_body( CallGraphNode::InputParameter { type_: input_type, .. } => { - let parameter_name = parameter_bindings[&input_type.canonicalize()].clone(); + let parameter_name = parameter_bindings + [&input_type.canonicalize(krate_collection)] + .clone(); blocks.insert(current_index, Fragment::VariableReference(parameter_name)); } CallGraphNode::MatchBranching => { @@ -395,6 +404,7 @@ fn _codegen_callable_closure_body( component_db, computation_db, &mut variant_name_generator, + krate_collection, &mut at_most_once_constructor_blocks, &mut variant_blocks, &mut new_dfs, diff --git a/compiler/pavexc/src/compiler/analyses/call_graph/core_graph.rs b/compiler/pavexc/src/compiler/analyses/call_graph/core_graph.rs index dd0fc59db..4257839e6 100644 --- a/compiler/pavexc/src/compiler/analyses/call_graph/core_graph.rs +++ b/compiler/pavexc/src/compiler/analyses/call_graph/core_graph.rs @@ -18,6 +18,7 @@ use crate::compiler::analyses::constructibles::ConstructibleDb; use crate::compiler::analyses::user_components::ScopeId; use crate::compiler::computation::{Computation, MatchResultVariant}; use crate::language::{Lifetime, Type, TypeReference}; +use crate::rustdoc::CrateCollection; use super::dependency_graph::DependencyGraph; @@ -66,6 +67,7 @@ pub(super) fn build_call_graph( computation_db: &ComputationDb, component_db: &ComponentDb, constructible_db: &ConstructibleDb, + krate_collection: &CrateCollection, lifecycle2n_allowed_invocations: F, diagnostics: &crate::diagnostic::DiagnosticSink, ) -> Result @@ -78,6 +80,7 @@ where computation_db, component_db, constructible_db, + krate_collection, error_observer_ids, lifecycle2n_allowed_invocations.clone(), ) @@ -233,9 +236,12 @@ where } }; for input_type in input_types { - if let Some((constructor_id, consumption_mode)) = - constructible_db.get(root_scope_id, &input_type, component_db.scope_graph()) - { + if let Some((constructor_id, consumption_mode)) = constructible_db.get( + root_scope_id, + &input_type, + component_db.scope_graph(), + krate_collection, + ) { nodes_to_be_visited.insert(VisitorStackElement { node_index: add_node_for_component( &mut call_graph, diff --git a/compiler/pavexc/src/compiler/analyses/call_graph/dependency_graph.rs b/compiler/pavexc/src/compiler/analyses/call_graph/dependency_graph.rs index 6cdd55a5a..390f4ac5d 100644 --- a/compiler/pavexc/src/compiler/analyses/call_graph/dependency_graph.rs +++ b/compiler/pavexc/src/compiler/analyses/call_graph/dependency_graph.rs @@ -6,6 +6,7 @@ use pavex_bp_schema::Lifecycle; use petgraph::stable_graph::{NodeIndex, StableDiGraph}; use crate::compiler::analyses::components::{ComponentDb, ComponentId}; +use crate::rustdoc::CrateCollection; use crate::{ compiler::{ analyses::{ @@ -37,6 +38,7 @@ impl DependencyGraph { computation_db: &ComputationDb, component_db: &ComponentDb, constructible_db: &ConstructibleDb, + krate_collection: &CrateCollection, error_observer_ids: &[ComponentId], lifecycle2n_allowed_invocations: F, ) -> Self @@ -175,6 +177,7 @@ impl DependencyGraph { component_scope, &input_type, component_db.scope_graph(), + krate_collection, ) { nodes_to_be_visited.insert(VisitorStackElement { component_id: constructor_id, diff --git a/compiler/pavexc/src/compiler/analyses/call_graph/request_scoped.rs b/compiler/pavexc/src/compiler/analyses/call_graph/request_scoped.rs index 8a24fb603..b656d8835 100644 --- a/compiler/pavexc/src/compiler/analyses/call_graph/request_scoped.rs +++ b/compiler/pavexc/src/compiler/analyses/call_graph/request_scoped.rs @@ -47,6 +47,7 @@ pub(crate) fn request_scoped_ordered_call_graph( computation_db, component_db, constructible_db, + krate_collection, diagnostics, ) else { @@ -78,6 +79,7 @@ pub(crate) fn request_scoped_call_graph( computation_db: &mut ComputationDb, component_db: &mut ComponentDb, constructible_db: &ConstructibleDb, + krate_collection: &CrateCollection, diagnostics: &crate::diagnostic::DiagnosticSink, ) -> Result { let mut graph_root = String::new(); @@ -100,6 +102,7 @@ pub(crate) fn request_scoped_call_graph( computation_db, component_db, constructible_db, + krate_collection, diagnostics, )?; if component_db.is_pre_processing_middleware(root_component_id) { @@ -111,6 +114,7 @@ pub(crate) fn request_scoped_call_graph( computation_db, component_db, constructible_db, + krate_collection, diagnostics, ) } else { @@ -127,6 +131,7 @@ fn _request_scoped_call_graph( computation_db: &mut ComputationDb, component_db: &mut ComponentDb, constructible_db: &ConstructibleDb, + krate_collection: &CrateCollection, diagnostics: &crate::diagnostic::DiagnosticSink, ) -> Result { fn lifecycle2invocations(l: Lifecycle) -> Option { @@ -143,6 +148,7 @@ fn _request_scoped_call_graph( computation_db, component_db, constructible_db, + krate_collection, lifecycle2invocations, diagnostics, ) @@ -161,6 +167,7 @@ fn augment_preprocessing_graph( computation_db: &mut ComputationDb, component_db: &mut ComponentDb, constructible_db: &ConstructibleDb, + krate_collection: &CrateCollection, diagnostics: &crate::diagnostic::DiagnosticSink, ) -> Result { assert!(component_db.is_pre_processing_middleware(root_component_id)); @@ -235,6 +242,7 @@ fn augment_preprocessing_graph( computation_db, component_db, constructible_db, + krate_collection, diagnostics, ) } diff --git a/compiler/pavexc/src/compiler/analyses/components/db/mod.rs b/compiler/pavexc/src/compiler/analyses/components/db/mod.rs index 90383b4fc..7ab8a05d8 100644 --- a/compiler/pavexc/src/compiler/analyses/components/db/mod.rs +++ b/compiler/pavexc/src/compiler/analyses/components/db/mod.rs @@ -277,6 +277,7 @@ impl ComponentDb { needs_error_handler, computation_db, &mut error_handlers_db, + krate_collection, diagnostics, ); } @@ -574,6 +575,7 @@ impl ComponentDb { needs_error_handler: IndexSet, computation_db: &mut ComputationDb, error_handlers_db: &mut ErrorHandlersDb, + krate_collection: &CrateCollection, diagnostics: &crate::diagnostic::DiagnosticSink, ) { let default_fallback_handler = { @@ -595,7 +597,7 @@ impl ComponentDb { let error_type = get_err_variant(fallible_computation.output_type().unwrap()); if let Some(error_handler) = - error_handlers_db.get_or_try_bind(scope_id, error_type, self) + error_handlers_db.get_or_try_bind(scope_id, error_type, self, krate_collection) { if let ErrorHandlerEntry::Valid { error_handler, @@ -637,9 +639,12 @@ impl ComponentDb { ); } - if let Some(error_handler) = - error_handlers_db.get_or_try_bind(scope_id, &self.pavex_error, self) - { + if let Some(error_handler) = error_handlers_db.get_or_try_bind( + scope_id, + &self.pavex_error, + self, + krate_collection, + ) { if let ErrorHandlerEntry::Valid { error_handler, component_id, @@ -713,6 +718,7 @@ impl ComponentDb { &self.pavex_error, &self.pavex_response, framework_item_db, + krate_collection, ) { Err(e) => { Self::invalid_constructor( @@ -1166,13 +1172,22 @@ impl ComponentDb { error_ref_input_index, ) { Ok(e) => { - error_handlers_db.insert(e, scope_id, error_handler_user_component_id); + error_handlers_db.insert( + e, + scope_id, + error_handler_user_component_id, + krate_collection, + ); } Err(e) => { if let Some(error_input) = error_handler_callable.inputs().get(error_ref_input_index) { - error_handlers_db.insert_invalid(&error_input.type_, scope_id); + error_handlers_db.insert_invalid( + &error_input.type_, + scope_id, + krate_collection, + ); } Self::invalid_error_handler( e, @@ -1404,6 +1419,7 @@ impl ComponentDb { cloning_policy: CloningPolicy, computation_db: &mut ComputationDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, derived_from: Option, ) -> Result { let callable = computation_db[callable_id].to_owned(); @@ -1412,6 +1428,7 @@ impl ComponentDb { &self.pavex_error, &self.pavex_response, framework_item_db, + krate_collection, )?; let constructor_component = UnregisteredComponent::SyntheticConstructor { lifecycle, @@ -1889,6 +1906,7 @@ impl ComponentDb { bindings: &HashMap, computation_db: &mut ComputationDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> ComponentId { fn _get_root_component_id( component_id: ComponentId, @@ -1943,6 +1961,7 @@ impl ComponentDb { cloning_policy, computation_db, framework_item_db, + krate_collection, Some(unbound_root_id), ) .unwrap() diff --git a/compiler/pavexc/src/compiler/analyses/constructibles.rs b/compiler/pavexc/src/compiler/analyses/constructibles.rs index 4c50332b5..fbe1334f7 100644 --- a/compiler/pavexc/src/compiler/analyses/constructibles.rs +++ b/compiler/pavexc/src/compiler/analyses/constructibles.rs @@ -56,7 +56,7 @@ impl ConstructibleDb { framework_items_db: &FrameworkItemDb, diagnostics: &crate::diagnostic::DiagnosticSink, ) -> Self { - let mut self_ = Self::_build(component_db, computation_db); + let mut self_ = Self::_build(component_db, computation_db, krate_collection); self_.detect_missing_constructors( component_db, computation_db, @@ -65,22 +65,32 @@ impl ConstructibleDb { diagnostics, ); self_.verify_singleton_ambiguity(component_db, computation_db, diagnostics); - self_.verify_lifecycle_of_singleton_dependencies(component_db, computation_db, diagnostics); + self_.verify_lifecycle_of_singleton_dependencies( + component_db, + computation_db, + krate_collection, + diagnostics, + ); self_.error_observers_cannot_depend_on_fallible_components( component_db, computation_db, + krate_collection, diagnostics, ); self_ } - fn _build(component_db: &ComponentDb, computation_db: &ComputationDb) -> Self { + fn _build( + component_db: &ComponentDb, + computation_db: &ComputationDb, + krate_collection: &CrateCollection, + ) -> Self { let mut self_ = Self { scope_id2constructibles: IndexMap::new(), }; for (component_id, _) in component_db.constructors(computation_db) { - self_.insert(component_id, component_db, computation_db); + self_.insert(component_id, component_db, computation_db, krate_collection); } self_ } @@ -226,6 +236,7 @@ impl ConstructibleDb { component_db, computation_db, framework_items_db, + krate_collection, ) else { if let Some(user_component_id) = component_db.user_component_id(component_id) { self.missing_constructor( @@ -471,6 +482,7 @@ impl ConstructibleDb { &self, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, diagnostics: &crate::diagnostic::DiagnosticSink, ) { for (component_id, _) in component_db.iter() { @@ -480,9 +492,12 @@ impl ConstructibleDb { let component = component_db.hydrated_component(component_id, computation_db); let component_scope = component_db.scope_id(component_id); for input_type in component.input_types() { - if let Some((input_constructor_id, _)) = - self.get(component_scope, input_type, component_db.scope_graph()) - && component_db.lifecycle(input_constructor_id) == Lifecycle::RequestScoped + if let Some((input_constructor_id, _)) = self.get( + component_scope, + input_type, + component_db.scope_graph(), + krate_collection, + ) && component_db.lifecycle(input_constructor_id) == Lifecycle::RequestScoped { Self::singleton_must_not_depend_on_request_scoped( component_id, @@ -511,6 +526,7 @@ impl ConstructibleDb { &self, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, diagnostics: &crate::diagnostic::DiagnosticSink, ) { 'outer: for (error_observer_id, _) in component_db.iter() { @@ -535,6 +551,7 @@ impl ConstructibleDb { component_db.scope_id(error_observer_id), &input, component_db.scope_graph(), + krate_collection, ) else { continue 'inner; }; @@ -584,12 +601,13 @@ impl ConstructibleDb { scope_id: ScopeId, type_: &Type, scope_graph: &ScopeGraph, + krate_collection: &CrateCollection, ) -> Option<(ComponentId, ConsumptionMode)> { let mut fifo = VecDeque::with_capacity(1); fifo.push_back(scope_id); while let Some(scope_id) = fifo.pop_front() { if let Some(constructibles) = self.scope_id2constructibles.get(&scope_id) - && let Some(output) = constructibles.get(type_) + && let Some(output) = constructibles.get(type_, krate_collection) { return Some(output); } @@ -604,6 +622,7 @@ impl ConstructibleDb { component_id: ComponentId, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, ) { let component = component_db.hydrated_component(component_id, computation_db); assert!(matches!(component, HydratedComponent::Constructor(_))); @@ -613,7 +632,7 @@ impl ConstructibleDb { .entry(scope_id) .or_insert_with(ConstructiblesInScope::new); let output = component.output_type(); - scope_constructibles.insert(output.unwrap().to_owned(), component_id); + scope_constructibles.insert(output.unwrap().to_owned(), component_id, krate_collection); } /// Find the constructor for a given type in a given scope. @@ -634,6 +653,7 @@ impl ConstructibleDb { component_db: &mut ComponentDb, computation_db: &mut ComputationDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> Option<(ComponentId, ConsumptionMode)> { let mut fifo = VecDeque::with_capacity(1); fifo.push_back(scope_id); @@ -644,6 +664,7 @@ impl ConstructibleDb { component_db, computation_db, framework_item_db, + krate_collection, ) { return Some(output); @@ -960,15 +981,19 @@ impl ConstructiblesInScope { /// Retrieve the constructor for a given type, if it exists. /// Only searches concrete (non-templated) types. - fn get(&self, type_: &Type) -> Option<(ComponentId, ConsumptionMode)> { - let normalized = type_.canonicalize(); + fn get( + &self, + type_: &Type, + krate_collection: &CrateCollection, + ) -> Option<(ComponentId, ConsumptionMode)> { + let normalized = type_.canonicalize(krate_collection); if let Some(constructor_id) = self.concrete.get(&normalized).copied() { return Some((constructor_id, ConsumptionMode::Move)); } match type_ { Type::Reference(ref_) if !ref_.lifetime.is_static() => { - let normalized_inner = ref_.inner.canonicalize(); + let normalized_inner = ref_.inner.canonicalize(krate_collection); if let Some(constructor_id) = self.concrete.get(&normalized_inner).copied() { return Some(( constructor_id, @@ -996,8 +1021,9 @@ impl ConstructiblesInScope { component_db: &mut ComponentDb, computation_db: &mut ComputationDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> Option<(ComponentId, ConsumptionMode)> { - if let Some(output) = self.get(type_) { + if let Some(output) = self.get(type_, krate_collection) { return Some(output); } let matched = self @@ -1018,9 +1044,10 @@ impl ConstructiblesInScope { component_db, computation_db, framework_item_db, + krate_collection, &bindings, ); - let bound = self.get(type_); + let bound = self.get(type_, krate_collection); assert!( bound.is_some(), "I used {} as a templated constructor to build {} but the binding process didn't succeed as expected.\nBindings:\n{}", @@ -1042,6 +1069,7 @@ impl ConstructiblesInScope { component_db, computation_db, framework_item_db, + krate_collection, )?; let lifecycle = component_db.lifecycle(component_id); if ref_.is_mutable { @@ -1070,11 +1098,17 @@ impl ConstructiblesInScope { } /// Register a type and its constructor. - fn insert(&mut self, output: Type, component_id: ComponentId) { + fn insert( + &mut self, + output: Type, + component_id: ComponentId, + krate_collection: &CrateCollection, + ) { if output.is_a_template() { self.templated.insert(output, component_id); } else { - self.concrete.insert(output.canonicalize(), component_id); + self.concrete + .insert(output.canonicalize(krate_collection), component_id); } } @@ -1087,6 +1121,7 @@ impl ConstructiblesInScope { component_db: &mut ComponentDb, computation_db: &mut ComputationDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, bindings: &HashMap, ) { let bound_component_id = component_db.bind_generic_type_parameters( @@ -1094,6 +1129,7 @@ impl ConstructiblesInScope { bindings, computation_db, framework_item_db, + krate_collection, ); let mut derived_component_ids = component_db.derived_component_ids(bound_component_id); @@ -1102,8 +1138,10 @@ impl ConstructiblesInScope { for derived_component_id in derived_component_ids { let component = component_db.hydrated_component(derived_component_id, computation_db); if let HydratedComponent::Constructor(c) = component { - self.concrete - .insert(c.output_type().canonicalize(), derived_component_id); + self.concrete.insert( + c.output_type().canonicalize(krate_collection), + derived_component_id, + ); } } } diff --git a/compiler/pavexc/src/compiler/analyses/error_handlers.rs b/compiler/pavexc/src/compiler/analyses/error_handlers.rs index 4973786c7..44114f127 100644 --- a/compiler/pavexc/src/compiler/analyses/error_handlers.rs +++ b/compiler/pavexc/src/compiler/analyses/error_handlers.rs @@ -7,6 +7,7 @@ use crate::compiler::analyses::components::ComponentDb; use crate::compiler::analyses::user_components::{ScopeId, UserComponentId}; use crate::compiler::component::ErrorHandler; use crate::language::{CanonicalType, Type, TypeReference}; +use crate::rustdoc::CrateCollection; /// The set of error types that can be handled, for each scope. #[derive(Default)] @@ -36,22 +37,28 @@ impl ErrorHandlersDb { error_handler: ErrorHandler, scope_id: ScopeId, id: UserComponentId, + krate_collection: &CrateCollection, ) { let scope_handlers = self .scope_id2error_handlers .entry(scope_id) .or_insert_with(ErrorHandlersInScope::new); - scope_handlers.insert(error_handler, id); + scope_handlers.insert(error_handler, id, krate_collection); } /// Record that an error handler was registered for the given type, even /// though the callable didn't pass our validation checks. - pub(crate) fn insert_invalid(&mut self, error_type_ref: &Type, scope_id: ScopeId) { + pub(crate) fn insert_invalid( + &mut self, + error_type_ref: &Type, + scope_id: ScopeId, + krate_collection: &CrateCollection, + ) { let scope_handlers = self .scope_id2error_handlers .entry(scope_id) .or_insert_with(ErrorHandlersInScope::new); - scope_handlers.insert_invalid(error_type_ref); + scope_handlers.insert_invalid(error_type_ref, krate_collection); } /// Find the error handler for a given error type in a given scope. @@ -69,12 +76,13 @@ impl ErrorHandlersDb { scope_id: ScopeId, type_: &Type, component_db: &ComponentDb, + krate_collection: &CrateCollection, ) -> Option { let mut fifo = VecDeque::with_capacity(1); fifo.push_back(scope_id); while let Some(scope_id) = fifo.pop_front() { if let Some(handlers) = self.scope_id2error_handlers.get_mut(&scope_id) - && let Some(output) = handlers.get_or_try_bind(type_) + && let Some(output) = handlers.get_or_try_bind(type_, krate_collection) { return Some(output); } @@ -123,16 +131,24 @@ impl ErrorHandlersInScope { } /// Retrieve the handler for a given error type, if it exists. - fn get(&self, type_: &Type) -> Option<&ErrorHandlerEntry> { - self.concrete.get(&type_.canonicalize()) + fn get( + &self, + type_: &Type, + krate_collection: &CrateCollection, + ) -> Option<&ErrorHandlerEntry> { + self.concrete.get(&type_.canonicalize(krate_collection)) } /// Retrieve the handler for a given error type, if it exists. /// /// If it doesn't exist, check the templated handlers to see if there is one /// that can be specialized to handle the given type. - fn get_or_try_bind(&mut self, type_: &Type) -> Option { - if let Some(handler) = self.get(type_) { + fn get_or_try_bind( + &mut self, + type_: &Type, + krate_collection: &CrateCollection, + ) -> Option { + if let Some(handler) = self.get(type_, krate_collection) { return Some(handler.to_owned()); } let matched = self @@ -159,8 +175,8 @@ impl ErrorHandlersInScope { component_id, }); } - self.insert(bound_handler, component_id); - let bound = self.get(type_); + self.insert(bound_handler, component_id, krate_collection); + let bound = self.get(type_, krate_collection); assert!( bound.is_some(), "I used {:?} as a templated error handler to build {} but the binding process didn't succeed as expected.\nBindings:\n{}", @@ -176,7 +192,12 @@ impl ErrorHandlersInScope { } /// Register a type and its handler. - fn insert(&mut self, error_handler: ErrorHandler, component_id: UserComponentId) { + fn insert( + &mut self, + error_handler: ErrorHandler, + component_id: UserComponentId, + krate_collection: &CrateCollection, + ) { let error_type_ref = error_handler.error_type_ref(); let Type::Reference(TypeReference { inner, .. }) = error_type_ref else { unreachable!() @@ -189,13 +210,14 @@ impl ErrorHandlersInScope { if error_type.is_a_template() { self.templated.insert(error_type, entry); } else { - self.concrete.insert(error_type.canonicalize(), entry); + self.concrete + .insert(error_type.canonicalize(krate_collection), entry); } } /// Record that an error handler was registered for the given type, even /// though the callable didn't pass our validation checks. - fn insert_invalid(&mut self, error_type_ref: &Type) { + fn insert_invalid(&mut self, error_type_ref: &Type, krate_collection: &CrateCollection) { let Type::Reference(TypeReference { inner, .. }) = error_type_ref else { return; }; @@ -204,8 +226,10 @@ impl ErrorHandlersInScope { self.templated .insert(error_type, ErrorHandlerEntry::Invalid); } else { - self.concrete - .insert(error_type.canonicalize(), ErrorHandlerEntry::Invalid); + self.concrete.insert( + error_type.canonicalize(krate_collection), + ErrorHandlerEntry::Invalid, + ); } } } diff --git a/compiler/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs b/compiler/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs index 16ed81d7f..7ee47f923 100644 --- a/compiler/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs +++ b/compiler/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs @@ -15,6 +15,7 @@ use crate::compiler::analyses::framework_items::{FrameworkItemDb, FrameworkItemI use crate::compiler::analyses::processing_pipeline::RequestHandlerPipeline; use crate::compiler::analyses::processing_pipeline::pipeline::Binding; use crate::language::{GenericArgument, GenericLifetimeParameter, Type}; +use crate::rustdoc::CrateCollection; use self::application_state::ApplicationState; @@ -25,6 +26,7 @@ impl RequestHandlerPipeline { package_id2name: &BiHashMap, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, ) -> Result { let id2codegened_fn = { let mut id2codegened_fn = IndexMap::new(); @@ -35,8 +37,12 @@ impl RequestHandlerPipeline { } let fn_ = CodegenedFn { fn_: { - let mut f = - call_graph.codegen(package_id2name, component_db, computation_db)?; + let mut f = call_graph.codegen( + package_id2name, + component_db, + computation_db, + krate_collection, + )?; f.sig.ident = format_ident!("{}", ident); f.vis = Visibility::Inherited; f @@ -106,7 +112,7 @@ impl RequestHandlerPipeline { .iter() .map(|input_type| { match input_bindings - .get_expr_for_type(input_type) { + .get_expr_for_type(input_type, krate_collection) { None => { let bindings = input_bindings .0 @@ -123,7 +129,7 @@ impl RequestHandlerPipeline { } Some(i) => { let mut output = i.to_token_stream(); - if let Some(cloning_indexes) = stage.type2cloning_indexes.get(&input_type.canonicalize()) + if let Some(cloning_indexes) = stage.type2cloning_indexes.get(&input_type.canonicalize(krate_collection)) && cloning_indexes.contains(&index) { output = quote! { #i.clone() }; } @@ -232,7 +238,7 @@ impl RequestHandlerPipeline { // as they appear in the field definitions // to make sure that (possible) lifetime parameters // are aligned. - let Some(binding) = &bindings.find_exact_by_type(&input.type_) else { + let Some(binding) = &bindings.find_exact_by_type(&input.type_, krate_collection) else { panic!("Could not find field name for input type `{:?}` in `Next`'s state, `{:?}`", input.type_, next_state.field_bindings); }; let ty_ = binding.type_.syn_type(package_id2name); @@ -286,7 +292,7 @@ impl RequestHandlerPipeline { .input_parameters .iter() .map(|input| { - let Some(binding) = field_bindings.find_exact_by_type(&input.type_) else { + let Some(binding) = field_bindings.find_exact_by_type(&input.type_, krate_collection) else { panic!("Could not find field name for input type `{:?}` in `Next`'s state, `{:?}`", input, next_state.field_bindings); }; let ident = format_ident!("{}", binding.ident); @@ -371,6 +377,7 @@ impl CodegenedRequestHandlerPipeline { request_scoped_bindings: &BiHashMap, // The name of the variable that holds the application state. server_state_ident: &Ident, + krate_collection: &CrateCollection, ) -> TokenStream { let first_stage = &self.stages[0]; let entrypoint = &first_stage.fn_; @@ -402,10 +409,10 @@ impl CodegenedRequestHandlerPipeline { unreachable!("Generic types should have been resolved by now") } }; - let canonical_inner = inner_type.canonicalize(); - let canonical_type = type_.canonicalize(); + let canonical_inner = inner_type.canonicalize(krate_collection); + let canonical_type = type_.canonicalize(krate_collection); if let Some(field_name) = application_state.bindings().iter() - .find(|(_, t)| t.canonicalize() == canonical_inner) + .find(|(_, t)| t.canonicalize(krate_collection) == canonical_inner) .map(|(name, _)| name) { if is_shared_reference { quote! { @@ -423,14 +430,14 @@ impl CodegenedRequestHandlerPipeline { } } } else if let Some(field_name) = request_scoped_bindings.iter() - .find(|(_, t)| t.canonicalize() == canonical_type) + .find(|(_, t)| t.canonicalize(krate_collection) == canonical_type) .map(|(name, _)| name) { quote! { #field_name } } else { let Some(field_name) = request_scoped_bindings.iter() - .find(|(_, t)| t.canonicalize() == canonical_inner) + .find(|(_, t)| t.canonicalize(krate_collection) == canonical_inner) .map(|(name, _)| name) else { let rs_bindings = request_scoped_bindings .iter() @@ -470,36 +477,69 @@ impl CodegenedRequestHandlerPipeline { /// Returns `true` if the first stage of the pipeline (i.e. the entrypoint) needs the specified /// type as input. - pub(crate) fn needs_input_type(&self, input_type: &Type) -> bool { - let canonical_input = input_type.canonicalize(); + pub(crate) fn needs_input_type( + &self, + input_type: &Type, + krate_collection: &CrateCollection, + ) -> bool { + let canonical_input = input_type.canonicalize(krate_collection); self.stages[0].input_parameters.iter().any(|t| { - if t.canonicalize() == canonical_input { + if t.canonicalize(krate_collection) == canonical_input { return true; } if let Type::Reference(r) = t { - return r.inner.canonicalize() == canonical_input; + return r.inner.canonicalize(krate_collection) == canonical_input; } false }) } - pub(crate) fn needs_allowed_methods(&self, framework_item_db: &FrameworkItemDb) -> bool { - self.needs_framework_item(framework_item_db, FrameworkItemDb::allowed_methods_id()) + pub(crate) fn needs_allowed_methods( + &self, + framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, + ) -> bool { + self.needs_framework_item( + framework_item_db, + FrameworkItemDb::allowed_methods_id(), + krate_collection, + ) } - pub(crate) fn needs_url_params(&self, framework_item_db: &FrameworkItemDb) -> bool { - self.needs_framework_item(framework_item_db, FrameworkItemDb::url_params_id()) + pub(crate) fn needs_url_params( + &self, + framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, + ) -> bool { + self.needs_framework_item( + framework_item_db, + FrameworkItemDb::url_params_id(), + krate_collection, + ) } - pub(crate) fn needs_connection_info(&self, framework_item_db: &FrameworkItemDb) -> bool { - self.needs_framework_item(framework_item_db, FrameworkItemDb::connection_info_id()) + pub(crate) fn needs_connection_info( + &self, + framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, + ) -> bool { + self.needs_framework_item( + framework_item_db, + FrameworkItemDb::connection_info_id(), + krate_collection, + ) } - pub(crate) fn needs_matched_route(&self, framework_item_db: &FrameworkItemDb) -> bool { + pub(crate) fn needs_matched_route( + &self, + framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, + ) -> bool { self.needs_framework_item( framework_item_db, FrameworkItemDb::matched_route_template_id(), + krate_collection, ) } @@ -507,8 +547,9 @@ impl CodegenedRequestHandlerPipeline { &self, framework_item_db: &FrameworkItemDb, id: FrameworkItemId, + krate_collection: &CrateCollection, ) -> bool { - self.needs_input_type(framework_item_db.get_type(id)) + self.needs_input_type(framework_item_db.get_type(id), krate_collection) } } diff --git a/compiler/pavexc/src/compiler/analyses/processing_pipeline/pipeline.rs b/compiler/pavexc/src/compiler/analyses/processing_pipeline/pipeline.rs index cb2025cac..bd66dc56a 100644 --- a/compiler/pavexc/src/compiler/analyses/processing_pipeline/pipeline.rs +++ b/compiler/pavexc/src/compiler/analyses/processing_pipeline/pipeline.rs @@ -225,6 +225,7 @@ impl RequestHandlerPipeline { computation_db, component_db, constructible_db, + krate_collection, diagnostics, )?; @@ -308,6 +309,7 @@ impl RequestHandlerPipeline { component_db, constructible_db, framework_item_db, + krate_collection, ) { if stage_index != 0 { @@ -594,7 +596,7 @@ impl RequestHandlerPipeline { let indexes: BTreeSet<_> = consumers.into_iter().map(|v| v.middleware_index).collect(); - type2cloning_indexes.insert(ty_.canonicalize(), indexes); + type2cloning_indexes.insert(ty_.canonicalize(krate_collection), indexes); } stage.type2cloning_indexes = type2cloning_indexes; } @@ -665,6 +667,7 @@ impl RequestHandlerPipeline { component_db: &mut ComponentDb, constructible_db: &mut ConstructibleDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> Option { if !matches!( component_db.hydrated_component(middleware_id, computation_db), @@ -721,7 +724,12 @@ impl RequestHandlerPipeline { None, ) .unwrap(); - constructible_db.insert(next_state_constructor_id, component_db, computation_db); + constructible_db.insert( + next_state_constructor_id, + component_db, + computation_db, + krate_collection, + ); // Since we now have the concrete type of the generic in `Next<_>`, we can bind // the generic type parameter of the middleware to that concrete type. @@ -749,6 +757,7 @@ impl RequestHandlerPipeline { &bindings, computation_db, framework_item_db, + krate_collection, ); let HydratedComponent::WrappingMiddleware(bound_mw) = @@ -766,6 +775,7 @@ impl RequestHandlerPipeline { component_db, computation_db, framework_item_db, + krate_collection, ) .is_some() ); @@ -1005,12 +1015,16 @@ impl Bindings { /// - `&mut name` if the caller wants an instance of `&mut Foo` /// /// In the last case, the binding is automatically marked as mutable. - pub(crate) fn get_expr_for_type(&mut self, type_: &Type) -> Option { - let canonical_type = type_.canonicalize(); + pub(crate) fn get_expr_for_type( + &mut self, + type_: &Type, + krate_collection: &CrateCollection, + ) -> Option { + let canonical_type = type_.canonicalize(krate_collection); let binding = self .0 .iter() - .find(|binding| binding.type_.canonicalize() == canonical_type); + .find(|binding| binding.type_.canonicalize(krate_collection) == canonical_type); if let Some(binding) = binding { let ident: syn::Expr = syn::parse_str(&binding.ident).unwrap(); let block = quote! { #ident }; @@ -1023,11 +1037,11 @@ impl Bindings { return None; }; - let canonical_inner = ref_.inner.canonicalize(); + let canonical_inner = ref_.inner.canonicalize(krate_collection); if let Some(binding) = self .0 .iter_mut() - .find(|binding| binding.type_.canonicalize() == canonical_inner) + .find(|binding| binding.type_.canonicalize(krate_collection) == canonical_inner) { let mut_ = if ref_.is_mutable { binding.mutable = true; @@ -1048,11 +1062,10 @@ impl Bindings { lifetime: ref_.lifetime.clone(), inner: Box::new(ref_.inner.as_ref().clone()), }); - let canonical_new_ref = new_ref.canonicalize(); - let binding = self - .0 - .iter() - .find(|binding| binding.type_.canonicalize() == canonical_new_ref); + let canonical_new_ref = new_ref.canonicalize(krate_collection); + let binding = self.0.iter().find(|binding| { + binding.type_.canonicalize(krate_collection) == canonical_new_ref + }); if let Some(binding) = binding { let ident: syn::Expr = syn::parse_str(&binding.ident).unwrap(); let block = quote! { #ident }; @@ -1064,11 +1077,15 @@ impl Bindings { } /// Return the first binding with a given type. - pub(crate) fn find_exact_by_type(&self, type_: &Type) -> Option<&Binding> { - let canonical = type_.canonicalize(); + pub(crate) fn find_exact_by_type( + &self, + type_: &Type, + krate_collection: &CrateCollection, + ) -> Option<&Binding> { + let canonical = type_.canonicalize(krate_collection); self.0 .iter() - .find(|binding| binding.type_.canonicalize() == canonical) + .find(|binding| binding.type_.canonicalize(krate_collection) == canonical) } } diff --git a/compiler/pavexc/src/compiler/app.rs b/compiler/pavexc/src/compiler/app.rs index 6756d5dc9..5c7a5aeeb 100644 --- a/compiler/pavexc/src/compiler/app.rs +++ b/compiler/pavexc/src/compiler/app.rs @@ -46,6 +46,7 @@ pub struct App { codegen_deps: HashMap, component_db: ComponentDb, computation_db: ComputationDb, + krate_collection: CrateCollection, } impl App { @@ -194,6 +195,7 @@ impl App { application_state, application_config, codegen_deps, + krate_collection, }, diagnostics, )) @@ -227,6 +229,7 @@ impl App { &self.component_db, &self.computation_db, &self.framework_item_db, + &self.krate_collection, )?; Ok(GeneratedApp { lib_rs, diff --git a/compiler/pavexc/src/compiler/codegen/mod.rs b/compiler/pavexc/src/compiler/codegen/mod.rs index 0e2dc22dd..cbf51369c 100644 --- a/compiler/pavexc/src/compiler/codegen/mod.rs +++ b/compiler/pavexc/src/compiler/codegen/mod.rs @@ -27,7 +27,7 @@ use crate::compiler::analyses::router::Router; use crate::compiler::app::GENERATED_APP_PACKAGE_ID; use crate::compiler::computation::Computation; use crate::language::{Callable, GenericArgument, Type}; -use crate::rustdoc::{ALLOC_PACKAGE_ID_REPR, TOOLCHAIN_CRATES}; +use crate::rustdoc::{ALLOC_PACKAGE_ID_REPR, CrateCollection, TOOLCHAIN_CRATES}; use self::application_config::ApplicationConfig; @@ -51,6 +51,7 @@ pub(crate) fn codegen_app( component_db: &ComponentDb, computation_db: &ComputationDb, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> Result { let sdk_deps = ServerSdkDeps::new(codegen_deps, package_id2name); let application_state_def = define_application_state(application_state, package_id2name); @@ -72,6 +73,7 @@ pub(crate) fn codegen_app( package_id2name, component_db, computation_db, + krate_collection, )?; let application_state_new = get_application_state_new( &application_state_private_new, @@ -93,6 +95,7 @@ pub(crate) fn codegen_app( package_id2name, component_db, computation_db, + krate_collection, ) .map(|p| (*id, p)) }) @@ -124,6 +127,7 @@ pub(crate) fn codegen_app( request_scoped_framework_bindings, package_id2name, framework_item_db, + krate_collection, ); let code = quote! { //! Do NOT edit this code. diff --git a/compiler/pavexc/src/compiler/codegen/router.rs b/compiler/pavexc/src/compiler/codegen/router.rs index 14d8782eb..2a015fafc 100644 --- a/compiler/pavexc/src/compiler/codegen/router.rs +++ b/compiler/pavexc/src/compiler/codegen/router.rs @@ -19,6 +19,7 @@ use crate::{ router::{PathRouter, Router}, }, language::Type, + rustdoc::CrateCollection, utils::syn_debug_parse2, }; @@ -32,6 +33,7 @@ pub(super) fn codegen_router( request_scoped_bindings: &BiHashMap, package_id2name: &BiHashMap, framework_items_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> TokenStream { let struct_ = router_struct(router, sdk_deps); let impl_ = router_impl( @@ -42,6 +44,7 @@ pub(super) fn codegen_router( request_scoped_bindings, package_id2name, framework_items_db, + krate_collection, ); quote! { #struct_ @@ -81,6 +84,7 @@ fn router_impl( request_scoped_bindings: &BiHashMap, package_id2name: &BiHashMap, framework_items_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> TokenStream { let route_method_ident = format_ident!("route"); match router { @@ -102,6 +106,7 @@ fn router_impl( framework_items_db, package_id2name, sdk_deps, + krate_collection, ); route_request.vis = syn::Visibility::Public(Default::default()); route_request.sig.ident = route_method_ident; @@ -140,6 +145,7 @@ fn router_impl( framework_items_db, package_id2name, sdk_deps, + krate_collection, ); route_request.sig.ident = format_ident!("route_domain_{i}"); @@ -157,6 +163,7 @@ fn router_impl( framework_items_db, package_id2name, sdk_deps, + krate_collection, ); let router_new = { @@ -281,6 +288,7 @@ fn domain_router( framework_items_db: &FrameworkItemDb, package_id2name: &BiHashMap, sdk_deps: &ServerSdkDeps, + krate_collection: &CrateCollection, ) -> ItemFn { let http = sdk_deps.http_ident(); let hyper = sdk_deps.hyper_ident(); @@ -305,6 +313,7 @@ fn domain_router( &state, package_id2name, sdk_deps, + krate_collection, false, ); let request_head_id = FrameworkItemDb::request_head_id(); @@ -369,6 +378,7 @@ fn path_router( framework_item_db: &FrameworkItemDb, package_id2name: &BiHashMap, sdk_deps: &ServerSdkDeps, + krate_collection: &CrateCollection, ) -> ImplItemFn { static WELL_KNOWN_METHODS: Lazy> = Lazy::new(|| { HashSet::from_iter([ @@ -393,10 +403,14 @@ fn path_router( route_id2method_router.values().any(|r| { r.methods_and_pipelines .iter() - .any(|(_, p)| p.needs_framework_item(framework_item_db, id)) + .any(|(_, p)| p.needs_framework_item(framework_item_db, id, krate_collection)) || r.catch_all_pipeline - .needs_framework_item(framework_item_db, id) - }) || fallback_codegened_pipeline.needs_framework_item(framework_item_db, id) + .needs_framework_item(framework_item_db, id, krate_collection) + }) || fallback_codegened_pipeline.needs_framework_item( + framework_item_db, + id, + krate_collection, + ) }; let needs_request_body = needs_framework_item(FrameworkItemDb::raw_incoming_body_id()); @@ -455,15 +469,16 @@ fn path_router( application_state, request_scoped_bindings, &server_state_ident, + krate_collection, ); let mut framework_primitives = Vec::new(); - if pipeline.needs_allowed_methods(framework_item_db) { + if pipeline.needs_allowed_methods(framework_item_db, krate_collection) { framework_primitives.push(allowed_methods_init.clone()); } - if pipeline.needs_connection_info(framework_item_db) { + if pipeline.needs_connection_info(framework_item_db, krate_collection) { framework_primitives.push(connection_info_init.clone()); } - if pipeline.needs_matched_route(framework_item_db) { + if pipeline.needs_matched_route(framework_item_db, krate_collection) { framework_primitives.push(matched_route_init.clone()); } quote! { @@ -537,6 +552,7 @@ fn path_router( &server_state_ident, package_id2name, sdk_deps, + krate_collection, true, ); let connection_info_ident = if needs_connection_info { @@ -598,6 +614,7 @@ fn routing_failure_fallback_block( server_state_ident: &Ident, package_id2name: &BiHashMap, sdk_deps: &ServerSdkDeps, + krate_collection: &CrateCollection, return_: bool, ) -> TokenStream { let pavex = sdk_deps.pavex_ident(); @@ -605,9 +622,10 @@ fn routing_failure_fallback_block( application_state, request_scoped_bindings, server_state_ident, + krate_collection, ); let unmatched_route = fallback_codegened_pipeline - .needs_matched_route(framework_items_db) + .needs_matched_route(framework_items_db, krate_collection) .then(|| { let id = FrameworkItemDb::matched_route_template_id(); let ident = framework_items_db.get_binding(id); @@ -617,7 +635,7 @@ fn routing_failure_fallback_block( } }); let allowed_methods = fallback_codegened_pipeline - .needs_allowed_methods(framework_items_db) + .needs_allowed_methods(framework_items_db, krate_collection) .then(|| { let id = FrameworkItemDb::allowed_methods_id(); let ident = framework_items_db.get_binding(id); @@ -627,7 +645,7 @@ fn routing_failure_fallback_block( } }); let url_params = fallback_codegened_pipeline - .needs_url_params(framework_items_db) + .needs_url_params(framework_items_db, krate_collection) .then(|| { let id = FrameworkItemDb::url_params_id(); let ident = framework_items_db.get_binding(id); @@ -636,7 +654,7 @@ fn routing_failure_fallback_block( } }); let unwrap_connection_info = fallback_codegened_pipeline - .needs_connection_info(framework_items_db) + .needs_connection_info(framework_items_db, krate_collection) .then(|| { let connection_info_ident = framework_items_db.get_binding(FrameworkItemDb::connection_info_id()); quote! { diff --git a/compiler/pavexc/src/compiler/codegen/state.rs b/compiler/pavexc/src/compiler/codegen/state.rs index d756fd519..20f10f283 100644 --- a/compiler/pavexc/src/compiler/codegen/state.rs +++ b/compiler/pavexc/src/compiler/codegen/state.rs @@ -13,6 +13,7 @@ use crate::compiler::{ }, codegen_utils::VariableNameGenerator, }; +use crate::rustdoc::CrateCollection; use super::{ApplicationStateCallGraph, ComponentDb, Type, deps::ServerSdkDeps}; @@ -195,11 +196,13 @@ pub(super) fn get_application_state_private_new( package_id2name: &BiHashMap, component_db: &ComponentDb, computation_db: &ComputationDb, + krate_collection: &CrateCollection, ) -> Result { let mut function = application_state_call_graph.call_graph.codegen( package_id2name, component_db, computation_db, + krate_collection, )?; function.sig.ident = format_ident!("_new"); function.vis = syn::Visibility::Inherited; diff --git a/compiler/pavexc/src/compiler/codegen_utils.rs b/compiler/pavexc/src/compiler/codegen_utils.rs index 5ae8e58cd..81cde0a2e 100644 --- a/compiler/pavexc/src/compiler/codegen_utils.rs +++ b/compiler/pavexc/src/compiler/codegen_utils.rs @@ -10,6 +10,7 @@ use quote::{ToTokens, format_ident, quote}; use crate::compiler::analyses::call_graph::CallGraphEdgeMetadata; use crate::language::{Callable, CanonicalType, Lifetime, Type, TypeReference}; +use crate::rustdoc::CrateCollection; #[derive(Debug, Clone)] pub(crate) enum Fragment { @@ -55,6 +56,7 @@ pub(crate) fn codegen_call_block( blocks: &mut BTreeMap, variable_generator: &mut VariableNameGenerator, package_id2name: &BiHashMap, + krate_collection: &CrateCollection, ) -> Result where I: Iterator, @@ -140,14 +142,19 @@ where lifetime: lifetime.to_owned(), inner: inner.to_owned(), }) - .canonicalize(), + .canonicalize(krate_collection), tokens.clone(), ); } - dependency_bindings.insert(type_.canonicalize(), tokens); + dependency_bindings.insert(type_.canonicalize(krate_collection), tokens); } - let constructor_invocation = codegen_call(callable, &dependency_bindings, package_id2name); + let constructor_invocation = codegen_call( + callable, + &dependency_bindings, + package_id2name, + krate_collection, + ); let block: syn::Block = syn::parse2(quote! { { #before_block @@ -169,6 +176,7 @@ pub(crate) fn codegen_call( callable: &Callable, variable_bindings: &HashMap>, package_id2name: &BiHashMap, + krate_collection: &CrateCollection, ) -> TokenStream { let callable_path: syn::ExprPath = { let mut buffer = String::new(); @@ -185,7 +193,7 @@ pub(crate) fn codegen_call( .iter() .map(|input| { let field_name = format_ident!("{}", input.name.as_str()); - let canonical = input.type_.canonicalize(); + let canonical = input.type_.canonicalize(krate_collection); let binding = match variable_bindings.get(&canonical) { Some(tokens) => tokens, None => { @@ -223,7 +231,7 @@ pub(crate) fn codegen_call( } } else { let parameters = callable.input_types().map(|i| { - let canonical = i.canonicalize(); + let canonical = i.canonicalize(krate_collection); match variable_bindings.get(&canonical) { Some(tokens) => tokens, None => { diff --git a/compiler/pavexc/src/compiler/component/constructor.rs b/compiler/pavexc/src/compiler/component/constructor.rs index 0e6066ac1..b9a7c3b05 100644 --- a/compiler/pavexc/src/compiler/component/constructor.rs +++ b/compiler/pavexc/src/compiler/component/constructor.rs @@ -1,5 +1,6 @@ use crate::compiler::analyses::framework_items::FrameworkItemDb; use crate::compiler::component::CannotTakeMutReferenceError; +use crate::rustdoc::CrateCollection; use indexmap::IndexSet; use crate::compiler::computation::{Computation, MatchResult}; @@ -18,6 +19,7 @@ impl<'a> Constructor<'a> { pavex_error: &Type, pavex_response: &Type, framework_item_db: &FrameworkItemDb, + krate_collection: &CrateCollection, ) -> Result { if c.output_type().is_none() { return Err(ConstructorValidationError::CannotReturnTheUnitType); @@ -58,9 +60,9 @@ impl<'a> Constructor<'a> { return Err(ConstructorValidationError::CannotConstructPavexResponse); } - let canonical_output = output_type.canonicalize(); + let canonical_output = output_type.canonicalize(krate_collection); for (_, framework_primitive_type) in framework_item_db.iter() { - let canonical_framework = framework_primitive_type.canonicalize(); + let canonical_framework = framework_primitive_type.canonicalize(krate_collection); if canonical_output == canonical_framework { return Err( ConstructorValidationError::CannotConstructFrameworkPrimitive { @@ -69,7 +71,7 @@ impl<'a> Constructor<'a> { ); } if let Type::Reference(ref_type) = &output_type - && ref_type.inner.canonicalize() == canonical_framework + && ref_type.inner.canonicalize(krate_collection) == canonical_framework { return Err( ConstructorValidationError::CannotConstructFrameworkPrimitive { diff --git a/rustdoc/rustdoc_ir/src/canonical_path_resolver.rs b/rustdoc/rustdoc_ir/src/canonical_path_resolver.rs new file mode 100644 index 000000000..2797d4668 --- /dev/null +++ b/rustdoc/rustdoc_ir/src/canonical_path_resolver.rs @@ -0,0 +1,25 @@ +use rustdoc_ext::GlobalItemId; + +/// Resolves a [`GlobalItemId`] to the canonical shortest importable path for that item. +/// +/// Used by [`Type::canonicalize`](crate::Type::canonicalize) to rewrite +/// [`PathType::base_type`](crate::PathType::base_type) so that the same underlying type +/// reached via different exports canonicalizes to the same value. +/// +/// The trait lives in `rustdoc_ir` to avoid a dependency on `rustdoc_processor`; +/// `rustdoc_processor` provides the concrete implementation on `CrateCollection`. +pub trait CanonicalPathResolver { + /// Return the canonical shortest importable path for the given item, or `None` if + /// no canonical path is available. + fn canonical_path(&self, id: &GlobalItemId) -> Option>; +} + +/// A resolver that never rewrites paths. Intended for tests and for the rare call sites +/// that genuinely have no [`CrateCollection`](https://docs.rs/rustdoc_processor) available. +pub struct NoOpResolver; + +impl CanonicalPathResolver for NoOpResolver { + fn canonical_path(&self, _: &GlobalItemId) -> Option> { + None + } +} diff --git a/rustdoc/rustdoc_ir/src/lib.rs b/rustdoc/rustdoc_ir/src/lib.rs index ac2392948..668bd6455 100644 --- a/rustdoc/rustdoc_ir/src/lib.rs +++ b/rustdoc/rustdoc_ir/src/lib.rs @@ -1,6 +1,7 @@ mod array; mod callable; mod callable_path; +mod canonical_path_resolver; pub mod function_pointer; mod generic; mod generic_argument; @@ -25,6 +26,7 @@ pub use callable_path::{ EnumVariantConstructorPath, FreeFunctionPath, InherentMethodPath, StructLiteralPath, TraitMethodPath, }; +pub use canonical_path_resolver::{CanonicalPathResolver, NoOpResolver}; pub use function_pointer::{FunctionPointer, FunctionPointerInput}; pub use generic::Generic; pub use generic_argument::{ConstGenericArgument, GenericArgument, GenericLifetimeParameter}; diff --git a/rustdoc/rustdoc_ir/src/type_.rs b/rustdoc/rustdoc_ir/src/type_.rs index 4de92c534..1f0f11dff 100644 --- a/rustdoc/rustdoc_ir/src/type_.rs +++ b/rustdoc/rustdoc_ir/src/type_.rs @@ -2,10 +2,11 @@ use std::fmt::{Debug, Formatter}; use ahash::{HashMap, HashMapExt}; use indexmap::{IndexMap, IndexSet}; +use rustdoc_ext::GlobalItemId; use crate::generics_equivalence::UnassignedIdGenerator; use crate::{ - Array, FunctionPointer, FunctionPointerInput, Generic, GenericArgument, + Array, CanonicalPathResolver, FunctionPointer, FunctionPointerInput, Generic, GenericArgument, GenericLifetimeParameter, Lifetime, NamedLifetime, PathType, RawPointer, Slice, Tuple, Type, TypeReference, }; @@ -690,11 +691,16 @@ impl Type { /// - Unassigned generic type parameters: renamed with fresh positional names (A, B, ...), /// **preserving** identity (two occurrences of `T` both become `A`). /// - Static lifetimes and scalar primitives are preserved as-is. - pub fn canonicalize(&self) -> CanonicalType { + /// - For [`Type::Path`] and [`Type::TypeAlias`], when a `rustdoc_id` is available, the + /// `base_type` is rewritten to the canonical shortest importable path reported by + /// the supplied [`CanonicalPathResolver`]. This ensures that the same type reached + /// via different exports canonicalizes to the same value. + pub fn canonicalize(&self, resolver: &dyn CanonicalPathResolver) -> CanonicalType { let mut lifetime_counter = 0usize; let mut generic_counter = 0usize; let mut generic_name_map: HashMap = HashMap::new(); CanonicalType(self._canonicalize( + resolver, &mut lifetime_counter, &mut generic_counter, &mut generic_name_map, @@ -703,6 +709,7 @@ impl Type { fn _canonicalize( &self, + resolver: &dyn CanonicalPathResolver, lifetime_counter: &mut usize, generic_counter: &mut usize, generic_name_map: &mut HashMap, @@ -767,6 +774,7 @@ impl Type { .map(|arg| match arg { GenericArgument::TypeParameter(inner) => { GenericArgument::TypeParameter(inner._canonicalize( + resolver, lifetime_counter, generic_counter, generic_name_map, @@ -778,10 +786,18 @@ impl Type { GenericArgument::Const(_) => arg.clone(), }) .collect(); + let base_type = if let Some(rustdoc_id) = t.rustdoc_id { + let id = GlobalItemId::new(rustdoc_id, t.package_id.clone()); + resolver + .canonical_path(&id) + .unwrap_or_else(|| t.base_type.clone()) + } else { + t.base_type.clone() + }; let path = PathType { package_id: t.package_id.clone(), rustdoc_id: t.rustdoc_id, - base_type: t.base_type.clone(), + base_type, generic_arguments, }; if is_alias { @@ -794,6 +810,7 @@ impl Type { is_mutable: r.is_mutable, lifetime: canonicalize_lifetime(&r.lifetime, lifetime_counter), inner: Box::new(r.inner._canonicalize( + resolver, lifetime_counter, generic_counter, generic_name_map, @@ -803,11 +820,19 @@ impl Type { elements: t .elements .iter() - .map(|e| e._canonicalize(lifetime_counter, generic_counter, generic_name_map)) + .map(|e| { + e._canonicalize( + resolver, + lifetime_counter, + generic_counter, + generic_name_map, + ) + }) .collect(), }), Type::Slice(s) => Type::Slice(Slice { element_type: Box::new(s.element_type._canonicalize( + resolver, lifetime_counter, generic_counter, generic_name_map, @@ -815,6 +840,7 @@ impl Type { }), Type::Array(a) => Type::Array(Array { element_type: Box::new(a.element_type._canonicalize( + resolver, lifetime_counter, generic_counter, generic_name_map, @@ -824,6 +850,7 @@ impl Type { Type::RawPointer(r) => Type::RawPointer(RawPointer { is_mutable: r.is_mutable, inner: Box::new(r.inner._canonicalize( + resolver, lifetime_counter, generic_counter, generic_name_map, @@ -836,6 +863,7 @@ impl Type { .map(|input| FunctionPointerInput { name: None, type_: input.type_._canonicalize( + resolver, lifetime_counter, generic_counter, generic_name_map, @@ -843,7 +871,12 @@ impl Type { }) .collect(), output: fp.output.as_ref().map(|t| { - Box::new(t._canonicalize(lifetime_counter, generic_counter, generic_name_map)) + Box::new(t._canonicalize( + resolver, + lifetime_counter, + generic_counter, + generic_name_map, + )) }), abi: fp.abi.clone(), is_unsafe: fp.is_unsafe, diff --git a/rustdoc/rustdoc_processor/Cargo.toml b/rustdoc/rustdoc_processor/Cargo.toml index 8043365e0..3c663cb46 100644 --- a/rustdoc/rustdoc_processor/Cargo.toml +++ b/rustdoc/rustdoc_processor/Cargo.toml @@ -14,6 +14,7 @@ clippy = { large_enum_variant = "allow", result_large_err = "allow" } # Core dependencies rustdoc-types = { workspace = true } rustdoc_ext = { path = "../rustdoc_ext" } +rustdoc_ir = { workspace = true } # Package graph guppy = { workspace = true } diff --git a/rustdoc/rustdoc_processor/src/collection.rs b/rustdoc/rustdoc_processor/src/collection.rs index 00d529a3b..c026f9b1a 100644 --- a/rustdoc/rustdoc_processor/src/collection.rs +++ b/rustdoc/rustdoc_processor/src/collection.rs @@ -492,6 +492,14 @@ impl CrateCollection { } } +impl rustdoc_ir::CanonicalPathResolver for CrateCollection { + fn canonical_path(&self, id: &GlobalItemId) -> Option> { + self.get_canonical_path_by_global_type_id(id) + .ok() + .map(<[String]>::to_vec) + } +} + impl Drop for CrateCollection { fn drop(&mut self) { let access_log = std::mem::take(&mut self.access_log); diff --git a/rustdoc/rustdoc_resolver/src/resolve_type.rs b/rustdoc/rustdoc_resolver/src/resolve_type.rs index 3a9561c45..e0e4cc115 100644 --- a/rustdoc/rustdoc_resolver/src/resolve_type.rs +++ b/rustdoc/rustdoc_resolver/src/resolve_type.rs @@ -799,7 +799,7 @@ fn find_assoc_type_in_trait_impls( return Ok(None); }; - let resolved_self = resolved_self.canonicalize(); + let resolved_self = resolved_self.canonicalize(krate_collection); // The trait's implementations are local to the trait's crate. let trait_package_id = &trait_global_id.package_id; @@ -835,7 +835,7 @@ fn find_assoc_type_in_trait_impls( ) else { continue; }; - if resolved_for.canonicalize() != resolved_self { + if resolved_for.canonicalize(krate_collection) != resolved_self { continue; }