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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4892,6 +4892,7 @@ dependencies = [
name = "rustc_type_ir_macros"
version = "0.0.0"
dependencies = [
"indexmap",
"proc-macro2",
"quote",
"syn",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_type_ir_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ nightly = []

[dependencies]
# tidy-alphabetical-start
indexmap = "2.4.0"
proc-macro2 = "1"
quote = "1"
syn = { version = "2.0.9", features = ["full", "visit-mut"] }
Expand Down
111 changes: 91 additions & 20 deletions compiler/rustc_type_ir_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use indexmap::IndexSet;
use quote::{ToTokens, quote};
use syn::visit_mut::VisitMut;
use syn::{Attribute, parse_quote};
Expand All @@ -17,11 +18,24 @@ decl_derive!(
[GenericTypeVisitable] => customizable_type_visitable_derive
);

struct LiftedTy {
struct TransformedTy {
ty: syn::Type,
generic_parameter_bounds: Vec<syn::Ident>,
generic_parameter_bounds: IndexSet<syn::Ident>,
}

enum TypeParameterPath {
Interner,
GenericParameter(syn::Ident),
}

enum TypeParameterTransform {
Continue,
Stop,
}

type TypeParameterVisitor =
fn(TypeParameterPath, &mut syn::TypePath, &mut IndexSet<syn::Ident>) -> TypeParameterTransform;

fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool {
let mut ignored = false;
attrs.iter().for_each(|attr| {
Expand Down Expand Up @@ -91,6 +105,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke

s.add_where_predicate(parse_quote! { I: Interner });
s.add_bounds(synstructure::AddBounds::Fields);
let generic_parameters =
s.ast().generics.type_params().map(|ty| ty.ident.clone()).collect::<Vec<_>>();
let mut generic_parameter_bounds = IndexSet::new();
s.bind_with(|_| synstructure::BindStyle::Move);
let body_try_fold = s.each_variant(|vi| {
let bindings = vi.bindings();
Expand All @@ -101,6 +118,12 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
bind.to_token_stream()
} else {
for param in
type_foldable_generic_parameters(bind.ast().ty.clone(), &generic_parameters)
{
generic_parameter_bounds.insert(param);
}

quote! {
::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)?
}
Expand Down Expand Up @@ -129,6 +152,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
// to generate code for them.
s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity"));
s.add_bounds(synstructure::AddBounds::Fields);
for param in generic_parameter_bounds {
s.add_where_predicate(parse_quote! { #param: ::rustc_type_ir::TypeFoldable<I> });
}
s.bound_impl(
quote!(::rustc_type_ir::TypeFoldable<I>),
quote! {
Expand All @@ -149,6 +175,19 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
)
}

fn type_foldable_generic_parameters(
ty: syn::Type,
generic_parameters: &[syn::Ident],
) -> IndexSet<syn::Ident> {
transform_type_parameters(ty, generic_parameters, |path, _, generic_parameter_bounds| {
if let TypeParameterPath::GenericParameter(param) = path {
generic_parameter_bounds.insert(param);
}
TypeParameterTransform::Continue
})
.generic_parameter_bounds
}

/// `Lift_Generic` is specialised for structs/enums parameterised by an interner
/// `I: Interner`. It derives `Lift<J>` by rewriting interner associated types
/// from `I::Assoc` to `J::Assoc`. The required associated type lift bounds are
Expand Down Expand Up @@ -251,40 +290,72 @@ fn is_type_phantom(ty: &syn::Type) -> bool {
get_first_path_segment(ty).is_some_and(|segment| segment.ident == "PhantomData")
}

fn lift(mut ty: syn::Type, generic_parameters: &[syn::Ident]) -> LiftedTy {
struct ItoJ<'a> {
fn lift(ty: syn::Type, generic_parameters: &[syn::Ident]) -> TransformedTy {
transform_type_parameters(ty, generic_parameters, |path, ty, generic_parameter_bounds| {
match path {
TypeParameterPath::Interner => {
*ty.path.segments.first_mut().unwrap() = parse_quote! { J };
TypeParameterTransform::Continue
}
TypeParameterPath::GenericParameter(param) => {
generic_parameter_bounds.insert(param.clone());
*ty = parse_quote! { <#param as ::rustc_type_ir::lift::Lift<J>>::Lifted };
TypeParameterTransform::Stop
}
}
})
}

fn transform_type_parameters(
mut ty: syn::Type,
generic_parameters: &[syn::Ident],
visit: TypeParameterVisitor,
) -> TransformedTy {
struct TypeParameterTransformer<'a> {
generic_parameters: &'a [syn::Ident],
generic_parameter_bounds: Vec<syn::Ident>,
generic_parameter_bounds: IndexSet<syn::Ident>,
visit: TypeParameterVisitor,
}

impl VisitMut for ItoJ<'_> {
impl VisitMut for TypeParameterTransformer<'_> {
fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) {
if i.qself.is_none() {
let path = if i.qself.is_none() {
let segments_len = i.path.segments.len();
if let Some(first) = i.path.segments.first_mut() {
// Turn paths from `I` into `J`
i.path.segments.first().and_then(|first| {
if first.ident == "I" {
*first = parse_quote! { J };
Some(TypeParameterPath::Interner)
} else if segments_len == 1
&& matches!(first.arguments, syn::PathArguments::None)
&& self.generic_parameters.iter().any(|param| first.ident == *param)
&& self.generic_parameters.contains(&first.ident)
{
let ident = first.ident.clone();
if !self.generic_parameter_bounds.iter().any(|param| *param == ident) {
self.generic_parameter_bounds.push(ident.clone());
}

*i = parse_quote! { <#ident as ::rustc_type_ir::lift::Lift<J>>::Lifted };
return;
Some(TypeParameterPath::GenericParameter(first.ident.clone()))
} else {
None
}
})
} else {
None
};

if let Some(path) = path {
if let TypeParameterTransform::Stop =
(self.visit)(path, i, &mut self.generic_parameter_bounds)
{
return;
}
}

syn::visit_mut::visit_type_path_mut(self, i);
}
}
let mut visitor = ItoJ { generic_parameters, generic_parameter_bounds: Vec::new() };

let mut visitor = TypeParameterTransformer {
generic_parameters,
generic_parameter_bounds: IndexSet::new(),
visit,
};
visitor.visit_type_mut(&mut ty);
LiftedTy { ty, generic_parameter_bounds: visitor.generic_parameter_bounds }
TransformedTy { ty, generic_parameter_bounds: visitor.generic_parameter_bounds }
}

#[cfg(not(feature = "nightly"))]
Expand Down
Loading