Skip to content

Commit 9940d66

Browse files
davidtwcoJamieCunliffeSevenarthadamgemmelljacobbramley
committed
assert-instr: support type generics
SVE intrinsics have both type and const generics and so the `assert_instr` macro needs to be able to generate test cases with the type generics instantiated with the types provided in the attribute. Co-authored-by: Jamie Cunliffe <Jamie.Cunliffe@arm.com> Co-authored-by: Luca Vizzarro <Luca.Vizzarro@arm.com> Co-authored-by: Adam Gemmell <adam.gemmell@arm.com> Co-authored-by: Jacob Bramley <jacob.bramley@arm.com>
1 parent f3350a2 commit 9940d66

1 file changed

Lines changed: 65 additions & 14 deletions

File tree

  • crates/assert-instr-macro/src

crates/assert-instr-macro/src/lib.rs

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern crate quote;
1414

1515
use proc_macro2::TokenStream;
1616
use quote::ToTokens;
17+
use syn::spanned::Spanned;
1718

1819
#[proc_macro_attribute]
1920
pub fn assert_instr(
@@ -67,40 +68,70 @@ pub fn assert_instr(
6768
);
6869
let mut inputs = Vec::new();
6970
let mut input_vals = Vec::new();
70-
let mut const_vals = Vec::new();
71+
let mut param_vals = Vec::new();
7172
let ret = &func.sig.output;
7273
for arg in func.sig.inputs.iter() {
7374
let capture = match *arg {
74-
syn::FnArg::Typed(ref c) => c,
75+
syn::FnArg::Typed(ref c) => c.to_owned(),
7576
ref v => panic!(
7677
"arguments must not have patterns: `{:?}`",
7778
v.clone().into_token_stream()
7879
),
7980
};
80-
let ident = match *capture.pat {
81-
syn::Pat::Ident(ref i) => &i.ident,
81+
let ident = match capture.pat.as_ref() {
82+
syn::Pat::Ident(i) => &i.ident.to_owned(),
8283
_ => panic!("must have bare arguments"),
8384
};
84-
if let Some((_, tokens)) = invoc.args.iter().find(|a| *ident == a.0) {
85+
if let Some(&(_, ref tokens)) = invoc.args.iter().find(|a| *ident == a.0) {
8586
input_vals.push(quote! { #tokens });
8687
} else {
8788
inputs.push(capture);
8889
input_vals.push(quote! { #ident });
8990
}
9091
}
9192
for arg in func.sig.generics.params.iter() {
92-
let c = match *arg {
93-
syn::GenericParam::Const(ref c) => c,
93+
match *arg {
94+
syn::GenericParam::Const(ref c) => {
95+
if let Some((_, tokens)) = invoc.args.iter().find(|a| c.ident == a.0) {
96+
param_vals.push(quote! { #tokens });
97+
} else {
98+
panic!("const generics must have a value for tests");
99+
}
100+
}
101+
syn::GenericParam::Type(ref t) => {
102+
if let Some((_, tokens)) = invoc.args.iter().find(|a| t.ident == a.0)
103+
&& let syn::Expr::Path(syn::ExprPath { qself, path, .. }) = tokens
104+
{
105+
param_vals.push(syn::Token![_](tokens.span()).to_token_stream());
106+
107+
let generic_ty_value = syn::TypePath {
108+
qself: qself.clone(),
109+
path: path.clone(),
110+
};
111+
112+
// Replace any function arguments that use generic parameters with the
113+
// instantiation provided in the macro invocation.
114+
inputs.iter_mut().for_each(|arg| {
115+
update_type_path(arg.ty.as_mut(), |type_path: &mut syn::TypePath| {
116+
if let Some(syn::PathSegment {
117+
ident: last_ident, ..
118+
}) = type_path.path.segments.last_mut()
119+
{
120+
if *last_ident == t.ident {
121+
*type_path = generic_ty_value.to_owned()
122+
}
123+
}
124+
})
125+
});
126+
} else {
127+
panic!("type generics must have a type for tests");
128+
}
129+
}
94130
ref v => panic!(
95-
"only const generics are allowed: `{:?}`",
131+
"only type and const generics are allowed: `{:?}`",
96132
v.clone().into_token_stream()
97133
),
98134
};
99-
if let Some((_, tokens)) = invoc.args.iter().find(|a| c.ident == a.0) {
100-
const_vals.push(quote! { #tokens });
101-
} else {
102-
panic!("const generics must have a value for tests");
103-
}
104135
}
105136

106137
let attrs = func
@@ -138,7 +169,7 @@ pub fn assert_instr(
138169
#[unsafe(no_mangle)]
139170
#[inline(never)]
140171
pub unsafe extern #abi fn #shim_name(#(#inputs),*) #ret {
141-
#name::<#(#const_vals),*>(#(#input_vals),*)
172+
#name::<#(#param_vals),*>(#(#input_vals),*)
142173
}
143174
};
144175

@@ -222,3 +253,23 @@ where
222253
}
223254
}
224255
}
256+
257+
/// Calls `update` on type paths so that type generics can be replaced with the instantiation from
258+
/// the attribute.
259+
fn update_type_path<F>(ty: &mut syn::Type, update: F)
260+
where
261+
F: Fn(&mut syn::TypePath),
262+
{
263+
use syn::Type::*;
264+
match ty {
265+
Array(syn::TypeArray { elem, .. })
266+
| Group(syn::TypeGroup { elem, .. })
267+
| Paren(syn::TypeParen { elem, .. })
268+
| Ptr(syn::TypePtr { elem, .. })
269+
| Reference(syn::TypeReference { elem, .. })
270+
| Slice(syn::TypeSlice { elem, .. }) => update_type_path(elem.as_mut(), update),
271+
Path(path @ syn::TypePath { .. }) => update(path),
272+
Tuple(..) => panic!("tuples and generic types together are not yet supported"),
273+
_ => {}
274+
}
275+
}

0 commit comments

Comments
 (0)