@@ -12,7 +12,7 @@ use proc_macro2::Span;
1212use  syn:: { 
1313    parse:: { self ,  Parse } , 
1414    spanned:: Spanned , 
15-     FnArg ,  ItemFn ,  LitInt ,  LitStr ,  PathArguments ,  ReturnType ,  Type ,  Visibility , 
15+     FnArg ,  ItemFn ,  LitInt ,  LitStr ,  Path ,   PathArguments ,  ReturnType ,  Type ,  Visibility , 
1616} ; 
1717
1818use  proc_macro:: TokenStream ; 
@@ -313,12 +313,18 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
313313    res. parse ( ) . unwrap ( ) 
314314} 
315315
316- #[ derive( Clone ,  Copy ) ]  
316+ #[ derive( Clone ,  Copy ,   Debug ) ]  
317317enum  RiscvArch  { 
318318    Rv32 , 
319319    Rv64 , 
320320} 
321321
322+ #[ derive( Clone ,  Copy ,  Debug ) ]  
323+ enum  RiscvPacItem  { 
324+     ExternalInterrupt , 
325+     CoreInterrupt , 
326+ } 
327+ 
322328/// Size of the trap frame (in number of registers) 
323329const  TRAP_SIZE :  usize  = 16 ; 
324330
@@ -505,20 +511,122 @@ _continue_interrupt_trap:
505511} 
506512
507513#[ proc_macro_attribute]  
508- /// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. 
509- /// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. 
510- pub  fn  interrupt_riscv32 ( args :  TokenStream ,  input :  TokenStream )  -> TokenStream  { 
511-     interrupt ( args,  input,  RiscvArch :: Rv32 ) 
514+ /// Attribute to declare an exception handler. The function must have the signature `[unsafe] fn(&[mut] riscv_rt::TrapFrame) [-> !]`. 
515+ pub  fn  exception ( args :  TokenStream ,  input :  TokenStream )  -> TokenStream  { 
516+     let  f = parse_macro_input ! ( input as  ItemFn ) ; 
517+ 
518+     // check the function arguments 
519+     if  f. sig . inputs . len ( )  > 1  { 
520+         return  parse:: Error :: new ( 
521+             f. sig . inputs . span ( ) , 
522+             "`#[exception]` function must at have most one input argument" , 
523+         ) 
524+         . to_compile_error ( ) 
525+         . into ( ) ; 
526+     } 
527+ 
528+     if  let  Some ( param)  = f. sig . inputs . first ( )  { 
529+         let  first_param_type = match  param { 
530+             FnArg :: Typed ( t)  => * t. ty . clone ( ) , 
531+             _ => { 
532+                 return  parse:: Error :: new ( param. span ( ) ,  "invalid argument" ) 
533+                     . to_compile_error ( ) 
534+                     . into ( ) ; 
535+             } 
536+         } ; 
537+ 
538+         let  expected_types:  Vec < Type >  = vec ! [ 
539+             parse_quote!( & riscv_rt:: TrapFrame ) , 
540+             parse_quote!( & mut  riscv_rt:: TrapFrame ) , 
541+         ] ; 
542+ 
543+         if  !expected_types. iter ( ) . any ( |t| first_param_type == * t)  { 
544+             return  parse:: Error :: new ( 
545+                 first_param_type. span ( ) , 
546+                 "`#[exception]` function argument must be `&[mut] riscv_rt::TrapFrame`" , 
547+             ) 
548+             . to_compile_error ( ) 
549+             . into ( ) ; 
550+         } 
551+     } 
552+ 
553+     // check the function signature 
554+     let  valid_signature = f. sig . constness . is_none ( ) 
555+         && f. sig . asyncness . is_none ( ) 
556+         && f. vis  == Visibility :: Inherited 
557+         && f. sig . abi . is_none ( ) 
558+         && f. sig . generics . params . is_empty ( ) 
559+         && f. sig . generics . where_clause . is_none ( ) 
560+         && f. sig . variadic . is_none ( ) 
561+         && match  f. sig . output  { 
562+             ReturnType :: Default  => true , 
563+             ReturnType :: Type ( _,  ref  ty)  => matches ! ( * * ty,  Type :: Never ( _) ) , 
564+         } ; 
565+ 
566+     if  !valid_signature { 
567+         return  parse:: Error :: new ( 
568+             f. span ( ) , 
569+             "`#[exception]` function must have signature `[unsafe] fn(&riscv_rt::TrapFrame) [-> !]`" , 
570+         ) 
571+         . to_compile_error ( ) 
572+         . into ( ) ; 
573+     } 
574+ 
575+     let  int_path = parse_macro_input ! ( args as  Path ) ; 
576+     let  int_ident = & int_path. segments . last ( ) . unwrap ( ) . ident ; 
577+     let  export_name = format ! ( "{:#}" ,  int_ident) ; 
578+ 
579+     quote ! ( 
580+         // Compile-time check to ensure the interrupt path implements the CoreInterruptNumber trait 
581+         const  _:  fn ( )  = || { 
582+             fn  assert_impl<T :  riscv_rt:: ExceptionNumber >( _arg:  T )  { } 
583+             assert_impl( #int_path) ; 
584+         } ; 
585+ 
586+         #[ export_name = #export_name] 
587+         #f
588+     ) 
589+     . into ( ) 
590+ } 
591+ 
592+ #[ proc_macro_attribute]  
593+ /// Attribute to declare an core interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. 
594+ /// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly. 
595+ pub  fn  core_interrupt_riscv32 ( args :  TokenStream ,  input :  TokenStream )  -> TokenStream  { 
596+     let  arch = match  ( )  { 
597+         #[ cfg( feature = "v-trap" ) ]  
598+         ( )  => Some ( RiscvArch :: Rv32 ) , 
599+         #[ cfg( not( feature = "v-trap" ) ) ]  
600+         ( )  => None , 
601+     } ; 
602+     interrupt ( args,  input,  RiscvPacItem :: CoreInterrupt ,  arch) 
512603} 
513604
514605#[ proc_macro_attribute]  
515606/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. 
516- /// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. 
517- pub  fn  interrupt_riscv64 ( args :  TokenStream ,  input :  TokenStream )  -> TokenStream  { 
518-     interrupt ( args,  input,  RiscvArch :: Rv64 ) 
607+ /// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly. 
608+ pub  fn  core_interrupt_riscv64 ( args :  TokenStream ,  input :  TokenStream )  -> TokenStream  { 
609+     let  arch = match  ( )  { 
610+         #[ cfg( feature = "v-trap" ) ]  
611+         ( )  => Some ( RiscvArch :: Rv64 ) , 
612+         #[ cfg( not( feature = "v-trap" ) ) ]  
613+         ( )  => None , 
614+     } ; 
615+     interrupt ( args,  input,  RiscvPacItem :: CoreInterrupt ,  arch) 
519616} 
520617
521- fn  interrupt ( args :  TokenStream ,  input :  TokenStream ,  _arch :  RiscvArch )  -> TokenStream  { 
618+ #[ proc_macro_attribute]  
619+ /// Attribute to declare an external interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. 
620+ pub  fn  external_interrupt ( args :  TokenStream ,  input :  TokenStream )  -> TokenStream  { 
621+     interrupt ( args,  input,  RiscvPacItem :: ExternalInterrupt ,  None ) 
622+ } 
623+ 
624+ fn  interrupt ( 
625+     args :  TokenStream , 
626+     input :  TokenStream , 
627+     pac_item :  RiscvPacItem , 
628+     arch :  Option < RiscvArch > , 
629+ )  -> TokenStream  { 
522630    let  f = parse_macro_input ! ( input as  ItemFn ) ; 
523631
524632    // check the function arguments 
@@ -553,30 +661,35 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
553661        . into ( ) ; 
554662    } 
555663
556-     if  !args. is_empty ( )  { 
557-         return  parse:: Error :: new ( Span :: call_site ( ) ,  "This attribute accepts no arguments" ) 
558-             . to_compile_error ( ) 
559-             . into ( ) ; 
560-     } 
664+     let  int_path = parse_macro_input ! ( args as  Path ) ; 
665+     let  int_ident = & int_path. segments . last ( ) . unwrap ( ) . ident ; 
666+     let  export_name = format ! ( "{:#}" ,  int_ident) ; 
561667
562-     // XXX should we blacklist other attributes? 
563-     let  ident = & f. sig . ident ; 
564-     let  export_name = format ! ( "{:#}" ,  ident) ; 
668+     let  start_trap = match  arch { 
669+         Some ( RiscvArch :: Rv32 )  => start_interrupt_trap ( int_ident,  RiscvArch :: Rv32 ) , 
670+         Some ( RiscvArch :: Rv64 )  => start_interrupt_trap ( int_ident,  RiscvArch :: Rv64 ) , 
671+         None  => proc_macro2:: TokenStream :: new ( ) , 
672+     } ; 
565673
566-     # [ cfg ( not ( feature =  "v-trap" ) ) ] 
567-     let  start_trap = proc_macro2 :: TokenStream :: new ( ) ; 
568-     # [ cfg ( feature =  "v-trap" ) ] 
569-     let  start_trap =  start_interrupt_trap ( ident ,  _arch ) ; 
674+     let  pac_trait =  match  pac_item  { 
675+          RiscvPacItem :: ExternalInterrupt  =>  quote ! ( riscv_rt :: ExternalInterruptNumber ) , 
676+          RiscvPacItem :: CoreInterrupt  =>  quote ! ( riscv_rt :: CoreInterruptNumber ) , 
677+     } ; 
570678
571679    quote ! ( 
680+         // Compile-time check to ensure the interrupt path implements the CoreInterruptNumber trait 
681+         const  _:  fn ( )  = || { 
682+             fn  assert_impl<T :  #pac_trait>( _arg:  T )  { } 
683+             assert_impl( #int_path) ; 
684+         } ; 
685+ 
572686        #start_trap
573687        #[ export_name = #export_name] 
574688        #f
575689    ) 
576690    . into ( ) 
577691} 
578692
579- #[ cfg( feature = "v-trap" ) ]  
580693fn  start_interrupt_trap ( ident :  & syn:: Ident ,  arch :  RiscvArch )  -> proc_macro2:: TokenStream  { 
581694    let  interrupt = ident. to_string ( ) ; 
582695    let  width = match  arch { 
0 commit comments