Skip to content

Commit a124fb3

Browse files
committed
Auto merge of #141961 - matthiaskrgr:rollup-r09j2sp, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #141724 (fix(#141141): When expanding `PartialEq`, check equality of scalar types first.) - #141833 (`tests/ui`: A New Order [2/N]) - #141861 (Switch `x86_64-msvc-{1,2}` back to Windows Server 2025 images) - #141914 (redesign stage 0 std follow-ups) - #141918 (Deconstruct values in the THIR visitor) - #141923 (Update books) - #141931 (Deconstruct values in the THIR visitor) - #141956 (Remove two trait methods from cg_ssa) r? `@ghost` `@rustbot` modify labels: rollup
2 parents aae43c4 + 3772a16 commit a124fb3

File tree

34 files changed

+678
-246
lines changed

34 files changed

+678
-246
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,6 +2465,39 @@ impl TyKind {
24652465
None
24662466
}
24672467
}
2468+
2469+
/// Returns `true` if this type is considered a scalar primitive (e.g.,
2470+
/// `i32`, `u8`, `bool`, etc).
2471+
///
2472+
/// This check is based on **symbol equality** and does **not** remove any
2473+
/// path prefixes or references. If a type alias or shadowing is present
2474+
/// (e.g., `type i32 = CustomType;`), this method will still return `true`
2475+
/// for `i32`, even though it may not refer to the primitive type.
2476+
pub fn maybe_scalar(&self) -> bool {
2477+
let Some(ty_sym) = self.is_simple_path() else {
2478+
// unit type
2479+
return self.is_unit();
2480+
};
2481+
matches!(
2482+
ty_sym,
2483+
sym::i8
2484+
| sym::i16
2485+
| sym::i32
2486+
| sym::i64
2487+
| sym::i128
2488+
| sym::u8
2489+
| sym::u16
2490+
| sym::u32
2491+
| sym::u64
2492+
| sym::u128
2493+
| sym::f16
2494+
| sym::f32
2495+
| sym::f64
2496+
| sym::f128
2497+
| sym::char
2498+
| sym::bool
2499+
)
2500+
}
24682501
}
24692502

24702503
/// A pattern type pattern.

compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs

Lines changed: 158 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use crate::deriving::generic::ty::*;
88
use crate::deriving::generic::*;
99
use crate::deriving::{path_local, path_std};
1010

11+
/// Expands a `#[derive(PartialEq)]` attribute into an implementation for the
12+
/// target item.
1113
pub(crate) fn expand_deriving_partial_eq(
1214
cx: &ExtCtxt<'_>,
1315
span: Span,
@@ -16,62 +18,6 @@ pub(crate) fn expand_deriving_partial_eq(
1618
push: &mut dyn FnMut(Annotatable),
1719
is_const: bool,
1820
) {
19-
fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
20-
let base = true;
21-
let expr = cs_fold(
22-
true, // use foldl
23-
cx,
24-
span,
25-
substr,
26-
|cx, fold| match fold {
27-
CsFold::Single(field) => {
28-
let [other_expr] = &field.other_selflike_exprs[..] else {
29-
cx.dcx()
30-
.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
31-
};
32-
33-
// We received arguments of type `&T`. Convert them to type `T` by stripping
34-
// any leading `&`. This isn't necessary for type checking, but
35-
// it results in better error messages if something goes wrong.
36-
//
37-
// Note: for arguments that look like `&{ x }`, which occur with packed
38-
// structs, this would cause expressions like `{ self.x } == { other.x }`,
39-
// which isn't valid Rust syntax. This wouldn't break compilation because these
40-
// AST nodes are constructed within the compiler. But it would mean that code
41-
// printed by `-Zunpretty=expanded` (or `cargo expand`) would have invalid
42-
// syntax, which would be suboptimal. So we wrap these in parens, giving
43-
// `({ self.x }) == ({ other.x })`, which is valid syntax.
44-
let convert = |expr: &P<Expr>| {
45-
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
46-
&expr.kind
47-
{
48-
if let ExprKind::Block(..) = &inner.kind {
49-
// `&{ x }` form: remove the `&`, add parens.
50-
cx.expr_paren(field.span, inner.clone())
51-
} else {
52-
// `&x` form: remove the `&`.
53-
inner.clone()
54-
}
55-
} else {
56-
expr.clone()
57-
}
58-
};
59-
cx.expr_binary(
60-
field.span,
61-
BinOpKind::Eq,
62-
convert(&field.self_expr),
63-
convert(other_expr),
64-
)
65-
}
66-
CsFold::Combine(span, expr1, expr2) => {
67-
cx.expr_binary(span, BinOpKind::And, expr1, expr2)
68-
}
69-
CsFold::Fieldless => cx.expr_bool(span, base),
70-
},
71-
);
72-
BlockOrExpr::new_expr(expr)
73-
}
74-
7521
let structural_trait_def = TraitDef {
7622
span,
7723
path: path_std!(marker::StructuralPartialEq),
@@ -97,7 +43,9 @@ pub(crate) fn expand_deriving_partial_eq(
9743
ret_ty: Path(path_local!(bool)),
9844
attributes: thin_vec![cx.attr_word(sym::inline, span)],
9945
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
100-
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
46+
combine_substructure: combine_substructure(Box::new(|a, b, c| {
47+
BlockOrExpr::new_expr(get_substructure_equality_expr(a, b, c))
48+
})),
10149
}];
10250

10351
let trait_def = TraitDef {
@@ -113,3 +61,156 @@ pub(crate) fn expand_deriving_partial_eq(
11361
};
11462
trait_def.expand(cx, mitem, item, push)
11563
}
64+
65+
/// Generates the equality expression for a struct or enum variant when deriving
66+
/// `PartialEq`.
67+
///
68+
/// This function generates an expression that checks if all fields of a struct
69+
/// or enum variant are equal.
70+
/// - Scalar fields are compared first for efficiency, followed by compound
71+
/// fields.
72+
/// - If there are no fields, returns `true` (fieldless types are always equal).
73+
///
74+
/// Whether a field is considered "scalar" is determined by comparing the symbol
75+
/// of its type to a set of known scalar type symbols (e.g., `i32`, `u8`, etc).
76+
/// This check is based on the type's symbol.
77+
///
78+
/// ### Example 1
79+
/// ```
80+
/// #[derive(PartialEq)]
81+
/// struct i32;
82+
///
83+
/// // Here, `field_2` is of type `i32`, but since it's a user-defined type (not
84+
/// // the primitive), it will not be treated as scalar. The function will still
85+
/// // check equality of `field_2` first because the symbol matches `i32`.
86+
/// #[derive(PartialEq)]
87+
/// struct Struct {
88+
/// field_1: &'static str,
89+
/// field_2: i32,
90+
/// }
91+
/// ```
92+
///
93+
/// ### Example 2
94+
/// ```
95+
/// mod ty {
96+
/// pub type i32 = i32;
97+
/// }
98+
///
99+
/// // Here, `field_2` is of type `ty::i32`, which is a type alias for `i32`.
100+
/// // However, the function will not reorder the fields because the symbol for
101+
/// // `ty::i32` does not match the symbol for the primitive `i32`
102+
/// // ("ty::i32" != "i32").
103+
/// #[derive(PartialEq)]
104+
/// struct Struct {
105+
/// field_1: &'static str,
106+
/// field_2: ty::i32,
107+
/// }
108+
/// ```
109+
///
110+
/// For enums, the discriminant is compared first, then the rest of the fields.
111+
///
112+
/// # Panics
113+
///
114+
/// If called on static or all-fieldless enums/structs, which should not occur
115+
/// during derive expansion.
116+
fn get_substructure_equality_expr(
117+
cx: &ExtCtxt<'_>,
118+
span: Span,
119+
substructure: &Substructure<'_>,
120+
) -> P<Expr> {
121+
use SubstructureFields::*;
122+
123+
match substructure.fields {
124+
EnumMatching(.., fields) | Struct(.., fields) => {
125+
let combine = move |acc, field| {
126+
let rhs = get_field_equality_expr(cx, field);
127+
if let Some(lhs) = acc {
128+
// Combine the previous comparison with the current field
129+
// using logical AND.
130+
return Some(cx.expr_binary(field.span, BinOpKind::And, lhs, rhs));
131+
}
132+
// Start the chain with the first field's comparison.
133+
Some(rhs)
134+
};
135+
136+
// First compare scalar fields, then compound fields, combining all
137+
// with logical AND.
138+
return fields
139+
.iter()
140+
.filter(|field| !field.maybe_scalar)
141+
.fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine)
142+
// If there are no fields, treat as always equal.
143+
.unwrap_or_else(|| cx.expr_bool(span, true));
144+
}
145+
EnumDiscr(disc, match_expr) => {
146+
let lhs = get_field_equality_expr(cx, disc);
147+
let Some(match_expr) = match_expr else {
148+
return lhs;
149+
};
150+
// Compare the discriminant first (cheaper), then the rest of the
151+
// fields.
152+
return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone());
153+
}
154+
StaticEnum(..) => cx.dcx().span_bug(
155+
span,
156+
"unexpected static enum encountered during `derive(PartialEq)` expansion",
157+
),
158+
StaticStruct(..) => cx.dcx().span_bug(
159+
span,
160+
"unexpected static struct encountered during `derive(PartialEq)` expansion",
161+
),
162+
AllFieldlessEnum(..) => cx.dcx().span_bug(
163+
span,
164+
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
165+
),
166+
}
167+
}
168+
169+
/// Generates an equality comparison expression for a single struct or enum
170+
/// field.
171+
///
172+
/// This function produces an AST expression that compares the `self` and
173+
/// `other` values for a field using `==`. It removes any leading references
174+
/// from both sides for readability. If the field is a block expression, it is
175+
/// wrapped in parentheses to ensure valid syntax.
176+
///
177+
/// # Panics
178+
///
179+
/// Panics if there are not exactly two arguments to compare (should be `self`
180+
/// and `other`).
181+
fn get_field_equality_expr(cx: &ExtCtxt<'_>, field: &FieldInfo) -> P<Expr> {
182+
let [rhs] = &field.other_selflike_exprs[..] else {
183+
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
184+
};
185+
186+
cx.expr_binary(
187+
field.span,
188+
BinOpKind::Eq,
189+
wrap_block_expr(cx, peel_refs(&field.self_expr)),
190+
wrap_block_expr(cx, peel_refs(rhs)),
191+
)
192+
}
193+
194+
/// Removes all leading immutable references from an expression.
195+
///
196+
/// This is used to strip away any number of leading `&` from an expression
197+
/// (e.g., `&&&T` becomes `T`). Only removes immutable references; mutable
198+
/// references are preserved.
199+
fn peel_refs(mut expr: &P<Expr>) -> P<Expr> {
200+
while let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = &expr.kind {
201+
expr = &inner;
202+
}
203+
expr.clone()
204+
}
205+
206+
/// Wraps a block expression in parentheses to ensure valid AST in macro
207+
/// expansion output.
208+
///
209+
/// If the given expression is a block, it is wrapped in parentheses; otherwise,
210+
/// it is returned unchanged.
211+
fn wrap_block_expr(cx: &ExtCtxt<'_>, expr: P<Expr>) -> P<Expr> {
212+
if matches!(&expr.kind, ExprKind::Block(..)) {
213+
return cx.expr_paren(expr.span, expr);
214+
}
215+
expr
216+
}

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ pub(crate) struct FieldInfo {
284284
/// The expressions corresponding to references to this field in
285285
/// the other selflike arguments.
286286
pub other_selflike_exprs: Vec<P<Expr>>,
287+
pub maybe_scalar: bool,
287288
}
288289

289290
#[derive(Copy, Clone)]
@@ -1220,7 +1221,8 @@ impl<'a> MethodDef<'a> {
12201221

12211222
let self_expr = discr_exprs.remove(0);
12221223
let other_selflike_exprs = discr_exprs;
1223-
let discr_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
1224+
let discr_field =
1225+
FieldInfo { span, name: None, self_expr, other_selflike_exprs, maybe_scalar: true };
12241226

12251227
let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args)
12261228
.map(|(&ident, selflike_arg)| {
@@ -1533,6 +1535,7 @@ impl<'a> TraitDef<'a> {
15331535
name: struct_field.ident,
15341536
self_expr,
15351537
other_selflike_exprs,
1538+
maybe_scalar: struct_field.ty.peel_refs().kind.maybe_scalar(),
15361539
}
15371540
})
15381541
.collect()

compiler/rustc_codegen_gcc/src/debuginfo.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
5252
fn clear_dbg_loc(&mut self) {
5353
self.location = None;
5454
}
55-
56-
fn get_dbg_loc(&self) -> Option<Self::DILocation> {
57-
self.location
58-
}
5955
}
6056

6157
/// Generate the `debug_context` in an MIR Body.

compiler/rustc_codegen_gcc/src/intrinsic/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,11 +524,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
524524
cond
525525
}
526526

527-
fn type_test(&mut self, _pointer: Self::Value, _typeid: Self::Value) -> Self::Value {
528-
// Unsupported.
529-
self.context.new_rvalue_from_int(self.int_type, 0)
530-
}
531-
532527
fn type_checked_load(
533528
&mut self,
534529
_llvtable: Self::Value,

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,8 +1815,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18151815
let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
18161816
let dbg_loc = self.get_dbg_loc();
18171817

1818-
// Test whether the function pointer is associated with the type identifier.
1819-
let cond = self.type_test(llfn, typeid_metadata);
1818+
// Test whether the function pointer is associated with the type identifier using the
1819+
// llvm.type.test intrinsic. The LowerTypeTests link-time optimization pass replaces
1820+
// calls to this intrinsic with code to test type membership.
1821+
let typeid = self.get_metadata_value(typeid_metadata);
1822+
let cond = self.call_intrinsic("llvm.type.test", &[llfn, typeid]);
18201823
let bb_pass = self.append_sibling_block("type_test.pass");
18211824
let bb_fail = self.append_sibling_block("type_test.fail");
18221825
self.cond_br(cond, bb_pass, bb_fail);

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
147147
}
148148
}
149149

150+
impl<'ll> Builder<'_, 'll, '_> {
151+
pub(crate) fn get_dbg_loc(&self) -> Option<&'ll DILocation> {
152+
unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder) }
153+
}
154+
}
155+
150156
impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
151157
// FIXME(eddyb) find a common convention for all of the debuginfo-related
152158
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
@@ -209,10 +215,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
209215
}
210216
}
211217

212-
fn get_dbg_loc(&self) -> Option<&'ll DILocation> {
213-
unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder) }
214-
}
215-
216218
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
217219
gdb::insert_reference_to_gdb_debug_scripts_section_global(self)
218220
}

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -636,13 +636,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
636636
}
637637
}
638638

639-
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value {
640-
// Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time
641-
// optimization pass replaces calls to this intrinsic with code to test type membership.
642-
let typeid = self.get_metadata_value(typeid);
643-
self.call_intrinsic("llvm.type.test", &[pointer, typeid])
644-
}
645-
646639
fn type_checked_load(
647640
&mut self,
648641
llvtable: &'ll Value,

compiler/rustc_codegen_ssa/src/traits/debuginfo.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
8181
);
8282
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
8383
fn clear_dbg_loc(&mut self);
84-
fn get_dbg_loc(&self) -> Option<Self::DILocation>;
8584
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
8685
fn set_var_name(&mut self, value: Self::Value, name: &str);
8786
}

compiler/rustc_codegen_ssa/src/traits/intrinsic.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
2222
fn abort(&mut self);
2323
fn assume(&mut self, val: Self::Value);
2424
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
25-
/// Trait method used to test whether a given pointer is associated with a type identifier.
26-
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value;
2725
/// Trait method used to load a function while testing if it is associated with a type
2826
/// identifier.
2927
fn type_checked_load(

0 commit comments

Comments
 (0)