22// Distributed under the MIT license that can be found in the LICENSE file.
33
44use std:: any:: { Any , TypeId } ;
5- use std:: marker:: PhantomData ;
65use std:: ops:: Deref ;
76use std:: rc:: Rc ;
87
@@ -32,96 +31,96 @@ macro_rules! impl_fn_addr {
3231}
3332impl_fn_addr ! ( ) ;
3433
35- trait ErasedFn : Any {
36- fn addr ( & self ) -> usize ;
37- }
38-
39- impl < T : FnAddr + Any > ErasedFn for T {
40- fn addr ( & self ) -> usize {
41- self . fn_addr ( )
42- }
43- }
44-
4534pub struct FnPtr < T > {
46- original : Option < Rc < dyn ErasedFn > > ,
47- current_cast : Option < Rc < dyn ErasedFn > > ,
48- // FnPtr does not use T, hence wrap in PhantomData
49- _marker : PhantomData < T > ,
35+ // Address of the function the pointer was created from. 0 for null, which
36+ // is never the address of a function.
37+ addr : usize ,
38+ // The function as callable through T. None if the pointer was cast to T
39+ // without an adapter, when calling it is UB.
40+ current : Option < T > ,
41+ // The function the pointer was created from, kept type-erased once the
42+ // pointer is cast to a different type so that it can be cast back. While
43+ // the pointer keeps its type this is None (and `current` is the original),
44+ // so creating and copying function pointers does not allocate.
45+ original : Option < Rc < dyn Any > > ,
5046}
5147
5248impl < T > FnPtr < T > {
5349 #[ inline]
5450 pub fn null ( ) -> Self {
5551 FnPtr {
52+ addr : 0 ,
53+ current : None ,
5654 original : None ,
57- current_cast : None ,
58- _marker : PhantomData ,
5955 }
6056 }
6157
6258 #[ inline]
6359 pub fn is_null ( & self ) -> bool {
64- self . original . is_none ( )
60+ self . addr == 0
6561 }
6662}
6763
6864impl < T : FnAddr + ' static > FnPtr < T > {
65+ #[ inline]
6966 pub fn new ( f : T ) -> Self {
70- let rc: Rc < dyn ErasedFn > = Rc :: new ( f) ;
7167 FnPtr {
72- original : Some ( rc . clone ( ) ) ,
73- current_cast : Some ( rc ) ,
74- _marker : PhantomData ,
68+ addr : f . fn_addr ( ) ,
69+ current : Some ( f ) ,
70+ original : None ,
7571 }
7672 }
7773}
7874
79- impl < T : ' static > FnPtr < T > {
80- pub fn cast < U : FnAddr + ' static > ( & self , adapter : Option < U > ) -> FnPtr < U > {
81- let original = self . original . as_ref ( ) . expect ( "ub: null fn pointer cast" ) ;
75+ impl < T : FnAddr + Copy + ' static > FnPtr < T > {
76+ pub fn cast < U : FnAddr + Copy + ' static > ( & self , adapter : Option < U > ) -> FnPtr < U > {
77+ assert ! ( !self . is_null( ) , "ub: null fn pointer cast" ) ;
78+
79+ // The current function, if it already has type U.
80+ let current_as_u = self . current . as_ref ( ) . and_then ( |current| {
81+ let current: & dyn Any = current;
82+ current. downcast_ref :: < U > ( ) . copied ( )
83+ } ) ;
84+ // The function this pointer was created from, if it has type U. While
85+ // `original` is unset, that is the current function.
86+ let original_as_u = match & self . original {
87+ Some ( original) => original. downcast_ref :: < U > ( ) . copied ( ) ,
88+ None => current_as_u,
89+ } ;
8290
83- let current_cast = if self
84- . current_cast
85- . as_ref ( )
86- . is_some_and ( |rc| Any :: type_id ( & * * rc) == TypeId :: of :: < U > ( ) )
87- {
88- self . current_cast . clone ( )
89- } else if Any :: type_id ( & * * original) == TypeId :: of :: < U > ( ) {
90- Some ( original. clone ( ) )
91- } else {
92- adapter. map ( |a| Rc :: new ( a) as Rc < dyn ErasedFn > )
91+ let original = match & self . original {
92+ Some ( original) => Some ( original. clone ( ) ) ,
93+ // Casting to the same type keeps the pointer as is.
94+ None if TypeId :: of :: < T > ( ) == TypeId :: of :: < U > ( ) => None ,
95+ None => self . current . map ( |current| Rc :: new ( current) as Rc < dyn Any > ) ,
9396 } ;
9497
9598 FnPtr {
96- original : Some ( original . clone ( ) ) ,
97- current_cast ,
98- _marker : PhantomData ,
99+ addr : self . addr ,
100+ current : current_as_u . or ( original_as_u ) . or ( adapter ) ,
101+ original ,
99102 }
100103 }
101104}
102105
103106impl < T : ' static > Deref for FnPtr < T > {
104107 type Target = T ;
105108 fn deref ( & self ) -> & T {
106- if self . original . is_none ( ) {
109+ if self . is_null ( ) {
107110 panic ! ( "ub: null fn pointer call" ) ;
108111 }
109- let rc = self
110- . current_cast
112+ self . current
111113 . as_ref ( )
112- . expect ( "ub: calling through incompatible fn pointer type" ) ;
113- let any: & dyn Any = & * * rc;
114- any. downcast_ref :: < T > ( )
115- . expect ( "ub: fn pointer type mismatch" )
114+ . expect ( "ub: calling through incompatible fn pointer type" )
116115 }
117116}
118117
119- impl < T > Clone for FnPtr < T > {
118+ impl < T : Copy > Clone for FnPtr < T > {
120119 fn clone ( & self ) -> Self {
121120 FnPtr {
121+ addr : self . addr ,
122+ current : self . current ,
122123 original : self . original . clone ( ) ,
123- current_cast : self . current_cast . clone ( ) ,
124- _marker : PhantomData ,
125124 }
126125 }
127126}
@@ -134,19 +133,15 @@ impl<T> Default for FnPtr<T> {
134133
135134impl < T > PartialEq for FnPtr < T > {
136135 fn eq ( & self , other : & Self ) -> bool {
137- match ( & self . original , & other. original ) {
138- ( None , None ) => true ,
139- ( Some ( a) , Some ( b) ) => a. addr ( ) == b. addr ( ) ,
140- _ => false ,
141- }
136+ self . addr == other. addr
142137 }
143138}
144139
145140impl < T > Eq for FnPtr < T > { }
146141
147142impl < T : ' static > ByteRepr for FnPtr < T > { }
148143
149- impl < T : ' static > ErasedPtr for FnPtr < T > {
144+ impl < T : Copy + ' static > ErasedPtr for FnPtr < T > {
150145 fn as_bytes ( & self ) -> Ptr < u8 > {
151146 panic ! ( "byte view not supported on fn pointer" ) ;
152147 }
@@ -161,7 +156,7 @@ impl<T: 'static> ErasedPtr for FnPtr<T> {
161156 }
162157}
163158
164- impl < T : ' static > FnPtr < T > {
159+ impl < T : Copy + ' static > FnPtr < T > {
165160 pub fn to_any ( & self ) -> AnyPtr {
166161 AnyPtr {
167162 ptr : Rc :: new ( self . clone ( ) ) ,
@@ -170,7 +165,81 @@ impl<T: 'static> FnPtr<T> {
170165}
171166
172167impl AnyPtr {
173- pub fn cast_fn < T : ' static > ( & self ) -> Option < FnPtr < T > > {
168+ pub fn cast_fn < T : Copy + ' static > ( & self ) -> Option < FnPtr < T > > {
174169 self . ptr . as_any ( ) . downcast_ref :: < FnPtr < T > > ( ) . cloned ( )
175170 }
176171}
172+
173+ #[ cfg( test) ]
174+ mod tests {
175+ use super :: * ;
176+
177+ fn add_one ( x : i32 ) -> i32 {
178+ x + 1
179+ }
180+
181+ fn twice ( x : i32 ) -> i32 {
182+ x * 2
183+ }
184+
185+ #[ test]
186+ fn call_null_and_equality ( ) {
187+ let f = FnPtr :: < fn ( i32 ) -> i32 > :: new ( add_one) ;
188+ assert ! ( !f. is_null( ) ) ;
189+ assert_eq ! ( ( * f) ( 1 ) , 2 ) ;
190+ assert ! ( f == f. clone( ) ) ;
191+ assert ! ( f != FnPtr :: new( twice as fn ( i32 ) -> i32 ) ) ;
192+ assert ! ( FnPtr :: <fn ( i32 ) -> i32 >:: null( ) . is_null( ) ) ;
193+ assert ! ( FnPtr :: <fn ( i32 ) -> i32 >:: null( ) == FnPtr :: default ( ) ) ;
194+ assert ! ( f != FnPtr :: null( ) ) ;
195+ }
196+
197+ #[ test]
198+ #[ should_panic( expected = "ub: null fn pointer call" ) ]
199+ fn call_null_panics ( ) {
200+ let f = FnPtr :: < fn ( i32 ) -> i32 > :: null ( ) ;
201+ ( * f) ( 1 ) ;
202+ }
203+
204+ #[ test]
205+ #[ should_panic( expected = "ub: null fn pointer cast" ) ]
206+ fn cast_null_panics ( ) {
207+ FnPtr :: < fn ( i32 ) -> i32 > :: null ( ) . cast :: < fn ( u32 ) -> u32 > ( None ) ;
208+ }
209+
210+ #[ test]
211+ fn cast_with_adapter_and_back ( ) {
212+ let f = FnPtr :: < fn ( i32 ) -> i32 > :: new ( add_one) ;
213+ let g =
214+ f. cast :: < fn ( u32 ) -> u32 > ( Some ( ( |x : u32 | add_one ( x as i32 ) as u32 ) as fn ( u32 ) -> u32 ) ) ;
215+ assert_eq ! ( ( * g) ( 4 ) , 5 ) ;
216+ // Equality is by the original function.
217+ assert ! ( g == f. cast:: <fn ( u32 ) -> u32 >( None ) ) ;
218+ // Casting back recovers the original function, without an adapter.
219+ let h = g. cast :: < fn ( i32 ) -> i32 > ( None ) ;
220+ assert ! ( h == f) ;
221+ assert_eq ! ( ( * h) ( 4 ) , 5 ) ;
222+ // Casting to the same type keeps the pointer.
223+ let same = f. cast :: < fn ( i32 ) -> i32 > ( None ) ;
224+ assert_eq ! ( ( * same) ( 9 ) , 10 ) ;
225+ }
226+
227+ #[ test]
228+ #[ should_panic( expected = "ub: calling through incompatible fn pointer type" ) ]
229+ fn cast_without_adapter_cannot_be_called ( ) {
230+ let f = FnPtr :: < fn ( i32 ) -> i32 > :: new ( add_one) ;
231+ let g = f. cast :: < fn ( u32 ) -> u32 > ( None ) ;
232+ ( * g) ( 1 ) ;
233+ }
234+
235+ #[ test]
236+ fn any_ptr_roundtrip ( ) {
237+ let f = FnPtr :: < fn ( i32 ) -> i32 > :: new ( twice) ;
238+ let any = f. to_any ( ) ;
239+ assert ! ( !any. is_null( ) ) ;
240+ let back = any. cast_fn :: < fn ( i32 ) -> i32 > ( ) . unwrap ( ) ;
241+ assert_eq ! ( ( * back) ( 21 ) , 42 ) ;
242+ assert ! ( back == f) ;
243+ assert ! ( any. cast_fn:: <fn ( u8 ) >( ) . is_none( ) ) ;
244+ }
245+ }
0 commit comments