in checker/src/block_visitor.rs [3189:3314]
fn get_discriminator_info(
&mut self,
data: u128,
enum_ty_layout: &rustc_middle::ty::layout::TyAndLayout<'tcx>,
) -> (bool, u128, VariantIdx, bool) {
let discr_signed; // Whether the discriminant tag is signed or not
let discr_bits; // The actual representation of the discriminant tag
let discr_index; // The index of the discriminant in the enum definition
let discr_has_data; // A flag indicating if the enum constant has a sub-component
let discr_ty = enum_ty_layout.ty.discriminant_ty(self.bv.tcx);
let discr_ty_layout = self.type_visitor().layout_of(discr_ty).unwrap();
match enum_ty_layout.variants {
Variants::Single { index } => {
// The enum only contains one variant.
// Truncates the value of the discriminant to fit into the layout.
discr_signed = discr_ty_layout.abi.is_signed();
discr_bits = match enum_ty_layout
.ty
.discriminant_for_variant(self.bv.tcx, index)
{
Some(discr) => {
if discr_signed {
discr_ty_layout.size.sign_extend(discr.val)
} else {
discr_ty_layout.size.truncate(discr.val)
}
}
None => discr_ty_layout.size.truncate(index.as_u32() as u128),
};
discr_index = index;
// A single-variant enum can have niches if and only if this variant has a sub-component
// of some special types (such as char).
discr_has_data = enum_ty_layout.largest_niche.is_some();
}
Variants::Multiple {
ref tag,
ref tag_encoding,
ref variants,
..
} => {
// The enum contains multiple (more than one) variants.
// The discriminant tag can be defined explicitly, and it can be negative.
// Extracts the tag layout that indicates the tag's sign.
let tag_layout = self
.type_visitor()
.layout_of(tag.value.to_int_ty(self.bv.tcx))
.unwrap();
discr_signed = tag_layout.abi.is_signed();
match *tag_encoding {
TagEncoding::Direct => {
// Truncates the discriminant value to fit into the layout.
// But before the truncation, we have to extend it properly if the tag can be negative.
// For example, `std::cmp::Ordering::Less` is defined as -1, and its discriminant
// tag is internally represented as u128::MAX in the type definition.
// However, the constant literal might have a different memory layout.
// Currently, Rust encodes the constant `std::cmp::Ordering::Less` as 0xff.
// So we need to first sign_extend 0xff and then truncate to fit into discr_layout.
let v = if discr_signed {
tag_layout.size.sign_extend(data)
} else {
data
};
// Not clear what is going on here, but we can't return a variant index
// if the discriminant value (discr_bits) does not fall in the valid range.
if tag.valid_range.start <= tag.valid_range.end {
discr_bits = u128::clamp(v, tag.valid_range.start, tag.valid_range.end);
} else {
// No idea why the range specification goes from high to low, but it happens.
discr_bits = u128::clamp(v, tag.valid_range.end, tag.valid_range.start);
}
// Iterates through all the variant definitions to find the actual index.
discr_index = match enum_ty_layout.ty.kind() {
TyKind::Adt(adt, _) => adt
.discriminants(self.bv.tcx)
.find(|(_, var)| var.val == discr_bits),
TyKind::Generator(def_id, substs, _) => {
let substs = substs.as_generator();
substs
.discriminants(*def_id, self.bv.tcx)
.find(|(_, var)| var.val == discr_bits)
}
_ => assume_unreachable!(),
}
.unwrap()
.0;
discr_has_data = false;
}
TagEncoding::Niche {
dataful_variant,
ref niche_variants,
niche_start,
} => {
// A niche means that there are some optimizations in the enum layout.
// For example, a value of type Option<char> is encoded as a 32-bit integer.
// There exists one single data containing variant (Some(char)).
// Other variants are encoded as niches with tag values starting as niche_start.
let variants_start = niche_variants.start().as_u32();
let variant = if data >= niche_start {
discr_has_data = false;
let variant_index_relative = (data - niche_start) as u32;
let variant_index = variants_start + variant_index_relative;
VariantIdx::from_u32(variant_index)
} else {
discr_has_data = true;
let fields = &variants[dataful_variant].fields();
checked_assume!(
fields.count() == 1
&& fields.offset(0).bytes() == 0
&& fields.memory_index(0) == 0,
"the data containing variant should contain a single sub-component"
);
dataful_variant
};
discr_bits = discr_ty_layout.size.truncate(variant.as_u32() as u128);
discr_index = variant;
}
}
}
};
(discr_signed, discr_bits, discr_index, discr_has_data)
}