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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ pub(crate) fn runtime_singletons_can_be_cloned_if_needed<'a>(
| Type::Reference(_)
| Type::Slice(_)
| Type::Array(_)
| Type::RawPointer(_) => {
| Type::RawPointer(_)
| Type::FunctionPointer(_) => {
return None;
}
Type::Path(_) | Type::Tuple(_) => {}
Expand Down
13 changes: 13 additions & 0 deletions compiler/pavexc/src/compiler/analyses/application_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,19 @@ fn _field_name_candidate(ty_: &Type, strategy: NamingStrategy, candidate: &mut S
}
_field_name_candidate(&raw_pointer.inner, strategy, candidate);
}
Type::FunctionPointer(fp) => {
candidate.push_str("fn_");
for (i, input) in fp.inputs.iter().enumerate() {
if i > 0 {
candidate.push('_');
}
_field_name_candidate(input, strategy, candidate);
}
if let Some(output) = &fp.output {
candidate.push_str("_ret_");
_field_name_candidate(output, strategy, candidate);
}
}
Type::Generic(generic) => {
// We don't have unassigned generics in the application state, so this should never happen.
// But, should it happen, there's really no other way to name it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ impl CodegenedRequestHandlerPipeline {
| Type::Path(_)
| Type::Tuple(_)
| Type::ScalarPrimitive(_)
| Type::RawPointer(_) => type_,
| Type::RawPointer(_)
| Type::FunctionPointer(_) => type_,
Type::Generic(_) => {
unreachable!("Generic types should have been resolved by now")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,10 @@ impl RequestHandlerPipeline {
Type::RawPointer(_) => {
continue;
}
// Function pointers are trivially `Copy`, this analysis doesn't concern them.
Type::FunctionPointer(_) => {
continue;
}
// We'd never encounter a raw slice as input type.
Type::Slice(_) |
// All types are concrete at this stage.
Expand Down
8 changes: 8 additions & 0 deletions compiler/pavexc/src/compiler/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,14 @@ fn collect_type_package_ids(package_ids: &mut IndexSet<PackageId>, t: &Type) {
Type::RawPointer(r) => {
collect_type_package_ids(package_ids, &r.inner);
}
Type::FunctionPointer(fp) => {
for input in &fp.inputs {
collect_type_package_ids(package_ids, input);
}
if let Some(output) = &fp.output {
collect_type_package_ids(package_ids, output);
}
}
Type::Generic(_) | Type::ScalarPrimitive(_) => {}
}
}
3 changes: 2 additions & 1 deletion compiler/pavexc/src/compiler/codegen/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ pub(super) fn get_application_state_new(
| Type::Path(_)
| Type::Tuple(_)
| Type::ScalarPrimitive(_)
| Type::RawPointer(_) => type_,
| Type::RawPointer(_)
| Type::FunctionPointer(_) => type_,
Type::Generic(_) => {
unreachable!("Generic types should have been resolved by now")
}
Expand Down
1 change: 1 addition & 0 deletions compiler/pavexc/src/compiler/path_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ fn must_be_a_plain_struct(
Type::Slice(s) => format!("`{s:?}` is a slice"),
Type::Array(a) => format!("`{a:?}` is an array"),
Type::RawPointer(r) => format!("`{r:?}` is a raw pointer"),
Type::FunctionPointer(fp) => format!("`{fp:?}` is a function pointer"),
Type::Generic(_) => {
unreachable!()
}
Expand Down
12 changes: 12 additions & 0 deletions compiler/pavexc/src/compiler/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,18 @@ pub(crate) fn implements_trait(
}
// TODO: handle other traits
}
Type::FunctionPointer(_) => {
// Function pointers implement Copy, Clone, Send, Sync, and Unpin.
if expected_trait.base_type == COPY_TRAIT_PATH
|| expected_trait.base_type == CLONE_TRAIT_PATH
|| expected_trait.base_type == SEND_TRAIT_PATH
|| expected_trait.base_type == SYNC_TRAIT_PATH
|| expected_trait.base_type == UNPIN_TRAIT_PATH
{
return Ok(true);
}
// TODO: handle other traits
}
Type::Generic(_) => {
// TODO: handle blanket implementations. As a first approximation,
// we assume that if the type is generic, it implements all traits.
Expand Down
77 changes: 77 additions & 0 deletions compiler/pavexc/src/language/fq_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use bimap::BiHashMap;
use guppy::PackageId;
use itertools::Itertools;

use rustdoc_ir::function_pointer::write_fn_pointer_prefix;

use crate::language::Type;
use crate::language::resolved_type::{GenericArgument, Lifetime, ScalarPrimitive};

Expand Down Expand Up @@ -120,6 +122,7 @@ pub enum FQPathType {
Slice(FQSlice),
Array(FQArray),
RawPointer(FQRawPointer),
FunctionPointer(FQFunctionPointer),
}

impl From<Type> for FQPathType {
Expand Down Expand Up @@ -172,6 +175,12 @@ impl From<Type> for FQPathType {
is_mutable: r.is_mutable,
inner: Box::new((*r.inner).into()),
}),
Type::FunctionPointer(fp) => FQPathType::FunctionPointer(FQFunctionPointer {
inputs: fp.inputs.into_iter().map(|t| t.into()).collect(),
output: fp.output.map(|t| Box::new((*t).into())),
abi: fp.abi,
is_unsafe: fp.is_unsafe,
}),
Type::Generic(_) => {
// ResolvedPath doesn't support unassigned generic parameters.
unreachable!("UnassignedGeneric")
Expand Down Expand Up @@ -214,6 +223,14 @@ pub struct FQRawPointer {
pub inner: Box<FQPathType>,
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct FQFunctionPointer {
pub inputs: Vec<FQPathType>,
pub output: Option<Box<FQPathType>>,
pub abi: rustdoc_types::Abi,
pub is_unsafe: bool,
}

impl PartialEq for FQPath {
fn eq(&self, other: &Self) -> bool {
// Using destructuring syntax to make sure we get a compiler error
Expand Down Expand Up @@ -366,6 +383,7 @@ impl FQPathType {
FQPathType::Slice(s) => s.render_path(id2name, buffer),
FQPathType::Array(a) => a.render_path(id2name, buffer),
FQPathType::RawPointer(r) => r.render_path(id2name, buffer),
FQPathType::FunctionPointer(fp) => fp.render_path(id2name, buffer),
}
}

Expand All @@ -380,6 +398,7 @@ impl FQPathType {
FQPathType::Slice(s) => s.render_for_error(buffer),
FQPathType::Array(a) => a.render_for_error(buffer),
FQPathType::RawPointer(r) => r.render_for_error(buffer),
FQPathType::FunctionPointer(fp) => fp.render_for_error(buffer),
}
}
}
Expand Down Expand Up @@ -524,6 +543,42 @@ impl FQRawPointer {
}
}

impl FQFunctionPointer {
pub fn render_path(&self, id2name: &BiHashMap<PackageId, String>, buffer: &mut String) {
write_fn_pointer_prefix(buffer, &self.abi, self.is_unsafe).unwrap();
write!(buffer, "fn(").unwrap();
let mut inputs = self.inputs.iter().peekable();
while let Some(input) = inputs.next() {
input.render_path(id2name, buffer);
if inputs.peek().is_some() {
write!(buffer, ", ").unwrap();
}
}
write!(buffer, ")").unwrap();
if let Some(output) = &self.output {
write!(buffer, " -> ").unwrap();
output.render_path(id2name, buffer);
}
}

pub fn render_for_error(&self, buffer: &mut String) {
write_fn_pointer_prefix(buffer, &self.abi, self.is_unsafe).unwrap();
write!(buffer, "fn(").unwrap();
let mut inputs = self.inputs.iter().peekable();
while let Some(input) = inputs.next() {
input.render_for_error(buffer);
if inputs.peek().is_some() {
write!(buffer, ", ").unwrap();
}
}
write!(buffer, ")").unwrap();
if let Some(output) = &self.output {
write!(buffer, " -> ").unwrap();
output.render_for_error(buffer);
}
}
}

impl Display for FQPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let last_segment_index = self.segments.len().saturating_sub(1);
Expand Down Expand Up @@ -563,6 +618,9 @@ impl Display for FQPathType {
FQPathType::RawPointer(r) => {
write!(f, "{r}")
}
FQPathType::FunctionPointer(fp) => {
write!(f, "{fp}")
}
}
}
}
Expand Down Expand Up @@ -606,6 +664,25 @@ impl Display for FQRawPointer {
}
}

impl Display for FQFunctionPointer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write_fn_pointer_prefix(f, &self.abi, self.is_unsafe)?;
write!(f, "fn(")?;
let last_input_index = self.inputs.len().saturating_sub(1);
for (i, input) in self.inputs.iter().enumerate() {
write!(f, "{input}")?;
if i != last_input_index && !self.inputs.is_empty() {
write!(f, ", ")?;
}
}
write!(f, ")")?;
if let Some(output) = &self.output {
write!(f, " -> {output}")?;
}
Ok(())
}
}

impl Display for FQTuple {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "(")?;
Expand Down
20 changes: 19 additions & 1 deletion compiler/pavexc/src/language/fq_path_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustdoc_types::ItemEnum;

use crate::language::callable_path::{CallPathGenericArgument, CallPathLifetime, CallPathType};
use crate::language::krate_name::dependency_name2package_id;
use crate::language::resolved_type::{Array, GenericArgument, Slice};
use crate::language::resolved_type::{Array, FunctionPointer, GenericArgument, Slice};
use crate::language::{CallPath, InvalidCallPath, RawPointer, Tuple, Type, TypeReference};
use crate::rustdoc::{
CannotGetCrateData, CrateCollection, CrateCollectionExt, GlobalItemId, ResolvedItem,
Expand Down Expand Up @@ -145,6 +145,24 @@ pub fn resolve_fq_path_type(
inner: Box::new(inner),
}))
}
FQPathType::FunctionPointer(fp) => {
let inputs = fp
.inputs
.iter()
.map(|e| resolve_fq_path_type(e, krate_collection))
.collect::<Result<Vec<_>, _>>()?;
let output = fp
.output
.as_ref()
.map(|t| resolve_fq_path_type(t, krate_collection))
.transpose()?;
Ok(Type::FunctionPointer(FunctionPointer {
inputs,
output: output.map(Box::new),
abi: fp.abi.clone(),
is_unsafe: fp.is_unsafe,
}))
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions compiler/ui_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ members = [
"reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/generated_app",
"reflection/crate_resolution/transitive_dependencies_can_be_renamed",
"reflection/crate_resolution/transitive_dependencies_can_be_renamed/generated_app",
"reflection/function_pointers_are_supported",
"reflection/function_pointers_are_supported/generated_app",
"reflection/generic_parameters_can_come_from_another_crate",
"reflection/generic_parameters_can_come_from_another_crate/generated_app",
"reflection/ids_can_conflict_across_crates",
Expand Down Expand Up @@ -329,6 +331,8 @@ members = [
"reflection/tuples_are_supported/generated_app",
"reflection/type_alias_are_supported",
"reflection/type_alias_are_supported/generated_app",
"reflection/unions_are_supported",
"reflection/unions_are_supported/generated_app",
]
resolver = "3"
[workspace.package]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "app_9147b911"
version = "0.1.0"
edition.workspace = true

[lints.rust.unexpected_cfgs]
level = "allow"
check-cfg = ["cfg(pavex_ide_hint)"]

[dependencies]
workspace_hack = { version = "0.1", path = "../../workspace_hack" }

[dependencies.pavex]
workspace = true

[dependencies.pavex_cli_client]
workspace = true
Loading
Loading