1- use rustc_ast:: { BinOpKind , BorrowKind , Expr , ExprKind , MetaItem , Mutability , Safety } ;
1+ use rustc_ast:: {
2+ BinOpKind , BlockCheckMode , BorrowKind , Expr , ExprKind , GenericArg , MetaItem , MutTy , Mutability ,
3+ Safety , TyKind , UnsafeSource ,
4+ } ;
25use rustc_expand:: base:: { Annotatable , ExtCtxt } ;
3- use rustc_span:: { Span , sym} ;
6+ use rustc_span:: { Ident , Span , kw , sym} ;
47use thin_vec:: thin_vec;
58
69use crate :: deriving:: generic:: ty:: * ;
@@ -125,7 +128,7 @@ fn get_substructure_equality_expr(
125128) -> Box < Expr > {
126129 use SubstructureFields :: * ;
127130
128- match substructure. fields {
131+ let field_comparison = match substructure. fields {
129132 EnumMatching ( .., fields) | Struct ( .., fields) => {
130133 let combine = move |acc, field| {
131134 let rhs = get_field_equality_expr ( cx, field) ;
@@ -138,23 +141,35 @@ fn get_substructure_equality_expr(
138141 Some ( rhs)
139142 } ;
140143
141- // First compare scalar fields, then compound fields, combining all
142- // with logical AND.
143- return fields
144- . iter ( )
145- . filter ( |field| !field. maybe_scalar )
146- . fold ( fields. iter ( ) . filter ( |field| field. maybe_scalar ) . fold ( None , combine) , combine)
147- // If there are no fields, treat as always equal.
148- . unwrap_or_else ( || cx. expr_bool ( span, true ) ) ;
144+ // If there are no fields, return true immediately.
145+ // If there is just one, compare it.
146+ // Otherwise, try to do a bitwise comparison.
147+ match & fields[ ..] {
148+ [ ] => return cx. expr_bool ( span, true ) ,
149+ [ field] => return get_field_equality_expr ( cx, field) ,
150+ _ => {
151+ // First compare scalar fields, then compound fields, combining all
152+ // with logical AND.
153+ fields
154+ . iter ( )
155+ . filter ( |field| !field. maybe_scalar )
156+ . fold (
157+ fields. iter ( ) . filter ( |field| field. maybe_scalar ) . fold ( None , combine) ,
158+ combine,
159+ )
160+ . unwrap ( )
161+ }
162+ }
149163 }
150164 EnumDiscr ( disc, match_expr) => {
151165 let lhs = get_field_equality_expr ( cx, disc) ;
152- let Some ( match_expr) = match_expr else {
153- return lhs;
154- } ;
155- // Compare the discriminant first (cheaper), then the rest of the
156- // fields.
157- return cx. expr_binary ( disc. span , BinOpKind :: And , lhs, match_expr. clone ( ) ) ;
166+ if let Some ( match_expr) = match_expr {
167+ // Compare the discriminant first (cheaper), then the rest of the
168+ // fields.
169+ cx. expr_binary ( disc. span , BinOpKind :: And , lhs, match_expr. clone ( ) )
170+ } else {
171+ lhs
172+ }
158173 }
159174 StaticEnum ( ..) => cx. dcx ( ) . span_bug (
160175 span,
@@ -168,6 +183,55 @@ fn get_substructure_equality_expr(
168183 span,
169184 "unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion" ,
170185 ) ,
186+ } ;
187+
188+ if matches ! ( substructure. fields, Struct ( ..) ) {
189+ // Construct intrinsics::can_compare_bitwise<Self>()
190+ let self_ty = cx. ty_path ( cx. path_ident ( span, Ident :: with_dummy_span ( kw:: SelfUpper ) ) ) ;
191+ let path = cx. expr_path ( cx. path_all (
192+ span,
193+ true ,
194+ cx. std_path ( & [ sym:: intrinsics, sym:: can_compare_bitwise] ) ,
195+ vec ! [ GenericArg :: Type ( self_ty. clone( ) ) ] ,
196+ ) ) ;
197+ let cond = cx. expr_call ( span, path, thin_vec ! [ ] ) ;
198+
199+ let [ _self, rhs] = & substructure. selflike_args [ ..] else {
200+ cx. dcx ( ) . span_bug ( span, "not exactly 2 arguments in `derive(PartialEq)`" ) ;
201+ } ;
202+
203+ // Construct intrinsics::compare_bitwise(
204+ // self as *const Self as *const u8,
205+ // other as *const Self as *const u8,
206+ //
207+ // );
208+ let self_ptr_ty = cx. ty ( span, TyKind :: Ptr ( MutTy { ty : self_ty, mutbl : Mutability :: Not } ) ) ;
209+ let u8_ty = cx. ty_ident ( span, Ident :: with_dummy_span ( sym:: u8) ) ;
210+ let u8_ptr_ty = cx. ty ( span, TyKind :: Ptr ( MutTy { ty : u8_ty, mutbl : Mutability :: Not } ) ) ;
211+
212+ let self_ptr = cx. expr_cast ( span, cx. expr_self ( span) , self_ptr_ty. clone ( ) ) ;
213+ let self_ptr_u8 = cx. expr_cast ( span, self_ptr, u8_ptr_ty. clone ( ) ) ;
214+
215+ let other_ptr = cx. expr_cast ( span, rhs. clone ( ) , self_ptr_ty) ;
216+ let other_ptr_u8 = cx. expr_cast ( span, other_ptr, u8_ptr_ty) ;
217+
218+ let size_of_val = cx. std_path ( & [ sym:: intrinsics, sym:: size_of_val] ) ;
219+ let size = cx. expr_call_global ( span, size_of_val, thin_vec ! [ cx. expr_self( span) ] ) ;
220+
221+ let compare_bytes = cx. std_path ( & [ sym:: intrinsics, sym:: compare_bytes] ) ;
222+
223+ let call_compare_bytes =
224+ cx. expr_call_global ( span, compare_bytes, thin_vec ! [ self_ptr_u8, other_ptr_u8, size] ) ;
225+
226+ let mut call = cx. block_expr ( call_compare_bytes) ;
227+ call. rules = BlockCheckMode :: Unsafe ( UnsafeSource :: CompilerGenerated ) ;
228+ let call = cx. expr_block ( call) ;
229+
230+ let is_eq = cx. expr_binary ( span, BinOpKind :: Eq , call, cx. expr_i32 ( span, 0 ) ) ;
231+
232+ cx. expr_if ( span, cond, is_eq, Some ( field_comparison) )
233+ } else {
234+ field_comparison
171235 }
172236}
173237
0 commit comments