builtin-derive: tag → discriminant

This commit is contained in:
Ralf Jung 2024-04-14 11:31:51 +02:00
parent f3c6608861
commit ec3ac1dcd6
6 changed files with 107 additions and 107 deletions

View file

@ -181,8 +181,8 @@ fn cs_clone(
all_fields = af;
vdata = &variant.data;
}
EnumTag(..) | AllFieldlessEnum(..) => {
cx.dcx().span_bug(trait_span, format!("enum tags in `derive({name})`",))
EnumDiscr(..) | AllFieldlessEnum(..) => {
cx.dcx().span_bug(trait_span, format!("enum discriminants in `derive({name})`",))
}
StaticEnum(..) | StaticStruct(..) => {
cx.dcx().span_bug(trait_span, format!("associated function in `derive({name})`"))

View file

@ -20,12 +20,12 @@ pub fn expand_deriving_partial_ord(
Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std));
// Order in which to perform matching
let tag_then_data = if let Annotatable::Item(item) = item
let discr_then_data = if let Annotatable::Item(item) = item
&& let ItemKind::Enum(def, _) = &item.kind
{
let dataful: Vec<bool> = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect();
match dataful.iter().filter(|&&b| b).count() {
// No data, placing the tag check first makes codegen simpler
// No data, placing the discriminant check first makes codegen simpler
0 => true,
1..=2 => false,
_ => (0..dataful.len() - 1).any(|i| {
@ -50,7 +50,7 @@ pub fn expand_deriving_partial_ord(
attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
cs_partial_cmp(cx, span, substr, tag_then_data)
cs_partial_cmp(cx, span, substr, discr_then_data)
})),
};
@ -72,7 +72,7 @@ fn cs_partial_cmp(
cx: &ExtCtxt<'_>,
span: Span,
substr: &Substructure<'_>,
tag_then_data: bool,
discr_then_data: bool,
) -> BlockOrExpr {
let test_id = Ident::new(sym::cmp, span);
let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
@ -108,12 +108,12 @@ fn cs_partial_cmp(
// cmp => cmp
// }
// ```
// where `expr2` is `partial_cmp(self_tag, other_tag)`, and `expr1` is a `match`
// against the enum variants. This means that we begin by comparing the enum tags,
// where `expr2` is `partial_cmp(self_discr, other_discr)`, and `expr1` is a `match`
// against the enum variants. This means that we begin by comparing the enum discriminants,
// before either inspecting their contents (if they match), or returning
// the `cmp::Ordering` of comparing the enum tags.
// the `cmp::Ordering` of comparing the enum discriminants.
// ```
// match partial_cmp(self_tag, other_tag) {
// match partial_cmp(self_discr, other_discr) {
// Some(Ordering::Equal) => match (self, other) {
// (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
// (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0),
@ -126,12 +126,12 @@ fn cs_partial_cmp(
// ```
// match (self, other) {
// (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
// _ => partial_cmp(self_tag, other_tag)
// _ => partial_cmp(self_discr, other_discr)
// }
// ```
// Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354
if !tag_then_data
if !discr_then_data
&& let ExprKind::Match(_, arms, _) = &mut expr1.kind
&& let Some(last) = arms.last_mut()
&& let PatKind::Wild = last.pat.kind

View file

@ -53,7 +53,7 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
EnumMatching(_, v, fields) => (v.ident, &v.data, fields),
AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
EnumDiscr(..) | StaticStruct(..) | StaticEnum(..) => {
cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
}
};

View file

@ -21,7 +21,7 @@
//! `struct T(i32, char)`).
//! - `EnumMatching`, when `Self` is an enum and all the arguments are the
//! same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`)
//! - `EnumTag` when `Self` is an enum, for comparing the enum tags.
//! - `EnumDiscr` when `Self` is an enum, for comparing the enum discriminants.
//! - `StaticEnum` and `StaticStruct` for static methods, where the type
//! being derived upon is either an enum or struct respectively. (Any
//! argument with type Self is just grouped among the non-self
@ -143,11 +143,11 @@
//! )
//! ```
//!
//! For the tags,
//! For the discriminants,
//!
//! ```text
//! EnumTag(
//! &[<ident of self tag>, <ident of other tag>],
//! EnumDiscr(
//! &[<ident of self discriminant>, <ident of other discriminant>],
//! <expr to combine with>,
//! )
//! ```
@ -315,10 +315,10 @@ pub enum SubstructureFields<'a> {
/// variant.
EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo>),
/// The tag of an enum. The first field is a `FieldInfo` for the tags, as
/// The discriminant of an enum. The first field is a `FieldInfo` for the discriminants, as
/// if they were fields. The second field is the expression to combine the
/// tag expression with; it will be `None` if no match is necessary.
EnumTag(FieldInfo, Option<P<Expr>>),
/// discriminant expression with; it will be `None` if no match is necessary.
EnumDiscr(FieldInfo, Option<P<Expr>>),
/// A static method where `Self` is a struct.
StaticStruct(&'a ast::VariantData, StaticFields),
@ -1137,9 +1137,9 @@ impl<'a> MethodDef<'a> {
/// impl ::core::cmp::PartialEq for A {
/// #[inline]
/// fn eq(&self, other: &A) -> bool {
/// let __self_tag = ::core::intrinsics::discriminant_value(self);
/// let __arg1_tag = ::core::intrinsics::discriminant_value(other);
/// __self_tag == __arg1_tag
/// let __self_discr = ::core::intrinsics::discriminant_value(self);
/// let __arg1_discr = ::core::intrinsics::discriminant_value(other);
/// __self_discr == __arg1_discr
/// && match (self, other) {
/// (A::A2(__self_0), A::A2(__arg1_0)) => *__self_0 == *__arg1_0,
/// _ => true,
@ -1148,7 +1148,7 @@ impl<'a> MethodDef<'a> {
/// }
/// ```
///
/// Creates a tag check combined with a match for a tuple of all
/// Creates a discriminant check combined with a match for a tuple of all
/// `selflike_args`, with an arm for each variant with fields, possibly an
/// arm for each fieldless variant (if `unify_fieldless_variants` is not
/// `Unify`), and possibly a default arm.
@ -1169,7 +1169,7 @@ impl<'a> MethodDef<'a> {
let span = trait_.span;
let variants = &enum_def.variants;
// Traits that unify fieldless variants always use the tag(s).
// Traits that unify fieldless variants always use the discriminant(s).
let unify_fieldless_variants =
self.fieldless_variants_strategy == FieldlessVariantsStrategy::Unify;
@ -1199,25 +1199,25 @@ impl<'a> MethodDef<'a> {
//
// e.g. for `PartialEq::eq` builds two statements:
// ```
// let __self_tag = ::core::intrinsics::discriminant_value(self);
// let __arg1_tag = ::core::intrinsics::discriminant_value(other);
// let __self_discr = ::core::intrinsics::discriminant_value(self);
// let __arg1_discr = ::core::intrinsics::discriminant_value(other);
// ```
let get_tag_pieces = |cx: &ExtCtxt<'_>| {
let tag_idents: Vec<_> = prefixes
let get_discr_pieces = |cx: &ExtCtxt<'_>| {
let discr_idents: Vec<_> = prefixes
.iter()
.map(|name| Ident::from_str_and_span(&format!("{name}_tag"), span))
.map(|name| Ident::from_str_and_span(&format!("{name}_discr"), span))
.collect();
let mut tag_exprs: Vec<_> = tag_idents
let mut discr_exprs: Vec<_> = discr_idents
.iter()
.map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident)))
.collect();
let self_expr = tag_exprs.remove(0);
let other_selflike_exprs = tag_exprs;
let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
let self_expr = discr_exprs.remove(0);
let other_selflike_exprs = discr_exprs;
let discr_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
let tag_let_stmts: ThinVec<_> = iter::zip(&tag_idents, &selflike_args)
let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args)
.map(|(&ident, selflike_arg)| {
let variant_value = deriving::call_intrinsic(
cx,
@ -1229,7 +1229,7 @@ impl<'a> MethodDef<'a> {
})
.collect();
(tag_field, tag_let_stmts)
(discr_field, discr_let_stmts)
};
// There are some special cases involving fieldless enums where no
@ -1239,19 +1239,19 @@ impl<'a> MethodDef<'a> {
if variants.len() > 1 {
match self.fieldless_variants_strategy {
FieldlessVariantsStrategy::Unify => {
// If the type is fieldless and the trait uses the tag and
// If the type is fieldless and the trait uses the discriminant and
// there are multiple variants, we need just an operation on
// the tag(s).
let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
let mut tag_check = self.call_substructure_method(
// the discriminant(s).
let (discr_field, mut discr_let_stmts) = get_discr_pieces(cx);
let mut discr_check = self.call_substructure_method(
cx,
trait_,
type_ident,
nonselflike_args,
&EnumTag(tag_field, None),
&EnumDiscr(discr_field, None),
);
tag_let_stmts.append(&mut tag_check.0);
return BlockOrExpr(tag_let_stmts, tag_check.1);
discr_let_stmts.append(&mut discr_check.0);
return BlockOrExpr(discr_let_stmts, discr_check.1);
}
FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless => {
return self.call_substructure_method(
@ -1266,7 +1266,7 @@ impl<'a> MethodDef<'a> {
}
} else if variants.len() == 1 {
// If there is a single variant, we don't need an operation on
// the tag(s). Just use the most degenerate result.
// the discriminant(s). Just use the most degenerate result.
return self.call_substructure_method(
cx,
trait_,
@ -1380,22 +1380,22 @@ impl<'a> MethodDef<'a> {
cx.expr_match(span, match_arg, match_arms)
};
// If the trait uses the tag and there are multiple variants, we need
// to add a tag check operation before the match. Otherwise, the match
// If the trait uses the discriminant and there are multiple variants, we need
// to add a discriminant check operation before the match. Otherwise, the match
// is enough.
if unify_fieldless_variants && variants.len() > 1 {
let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
let (discr_field, mut discr_let_stmts) = get_discr_pieces(cx);
// Combine a tag check with the match.
let mut tag_check_plus_match = self.call_substructure_method(
// Combine a discriminant check with the match.
let mut discr_check_plus_match = self.call_substructure_method(
cx,
trait_,
type_ident,
nonselflike_args,
&EnumTag(tag_field, Some(get_match_expr(selflike_args))),
&EnumDiscr(discr_field, Some(get_match_expr(selflike_args))),
);
tag_let_stmts.append(&mut tag_check_plus_match.0);
BlockOrExpr(tag_let_stmts, tag_check_plus_match.1)
discr_let_stmts.append(&mut discr_check_plus_match.0);
BlockOrExpr(discr_let_stmts, discr_check_plus_match.1)
} else {
BlockOrExpr(ThinVec::new(), Some(get_match_expr(selflike_args)))
}
@ -1701,16 +1701,16 @@ where
rest.iter().rfold(base_expr, op)
}
}
EnumTag(tag_field, match_expr) => {
let tag_check_expr = f(cx, CsFold::Single(tag_field));
EnumDiscr(discr_field, match_expr) => {
let discr_check_expr = f(cx, CsFold::Single(discr_field));
if let Some(match_expr) = match_expr {
if use_foldl {
f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone()))
f(cx, CsFold::Combine(trait_span, discr_check_expr, match_expr.clone()))
} else {
f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr))
f(cx, CsFold::Combine(trait_span, match_expr.clone(), discr_check_expr))
}
} else {
tag_check_expr
discr_check_expr
}
}
StaticEnum(..) | StaticStruct(..) => {

View file

@ -66,9 +66,9 @@ fn hash_substructure(cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'
fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect();
(stmts, None)
}
EnumTag(tag_field, match_expr) => {
assert!(tag_field.other_selflike_exprs.is_empty());
let stmts = thin_vec![call_hash(tag_field.span, tag_field.self_expr.clone())];
EnumDiscr(discr_field, match_expr) => {
assert!(discr_field.other_selflike_exprs.is_empty());
let stmts = thin_vec![call_hash(discr_field.span, discr_field.self_expr.clone())];
(stmts, match_expr.clone())
}
_ => cx.dcx().span_bug(trait_span, "impossible substructure in `derive(Hash)`"),