1
Fork 0

Use AdtDef to check enum.

This commit is contained in:
Camille GILLOT 2022-10-30 18:31:03 +00:00
parent 65f77b7eb5
commit abc1ad7106
2 changed files with 65 additions and 62 deletions

View file

@ -6,7 +6,7 @@ use super::*;
use rustc_attr as attr; use rustc_attr as attr;
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::{ItemKind, Node, PathSegment}; use rustc_hir::{ItemKind, Node, PathSegment};
@ -75,7 +75,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_simd(tcx, span, def_id); check_simd(tcx, span, def_id);
} }
check_transparent(tcx, span, def); check_transparent(tcx, def);
check_packed(tcx, span, def); check_packed(tcx, span, def);
} }
@ -83,7 +83,7 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id); let def = tcx.adt_def(def_id);
let span = tcx.def_span(def_id); let span = tcx.def_span(def_id);
def.destructor(tcx); // force the destructor to be evaluated def.destructor(tcx); // force the destructor to be evaluated
check_transparent(tcx, span, def); check_transparent(tcx, def);
check_union_fields(tcx, span, def_id); check_union_fields(tcx, span, def_id);
check_packed(tcx, span, def); check_packed(tcx, span, def);
} }
@ -506,11 +506,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
tcx.ensure().typeck(id.owner_id.def_id); tcx.ensure().typeck(id.owner_id.def_id);
} }
DefKind::Enum => { DefKind::Enum => {
let item = tcx.hir().item(id); check_enum(tcx, id.owner_id.def_id);
let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
return;
};
check_enum(tcx, &enum_definition.variants, item.owner_id.def_id);
} }
DefKind::Fn => {} // entirely within check_item_body DefKind::Fn => {} // entirely within check_item_body
DefKind::Impl => { DefKind::Impl => {
@ -1026,7 +1022,7 @@ pub(super) fn check_packed_inner(
None None
} }
pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtDef<'tcx>) { pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) {
if !adt.repr().transparent() { if !adt.repr().transparent() {
return; return;
} }
@ -1035,14 +1031,14 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
feature_err( feature_err(
&tcx.sess.parse_sess, &tcx.sess.parse_sess,
sym::transparent_unions, sym::transparent_unions,
sp, tcx.def_span(adt.did()),
"transparent unions are unstable", "transparent unions are unstable",
) )
.emit(); .emit();
} }
if adt.variants().len() != 1 { if adt.variants().len() != 1 {
bad_variant_count(tcx, adt, sp, adt.did()); bad_variant_count(tcx, adt, tcx.def_span(adt.did()), adt.did());
if adt.variants().is_empty() { if adt.variants().is_empty() {
// Don't bother checking the fields. No variants (and thus no fields) exist. // Don't bother checking the fields. No variants (and thus no fields) exist.
return; return;
@ -1103,7 +1099,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
.filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
let non_zst_count = non_zst_fields.clone().count(); let non_zst_count = non_zst_fields.clone().count();
if non_zst_count >= 2 { if non_zst_count >= 2 {
bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did()));
} }
let incompatible_zst_fields = let incompatible_zst_fields =
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
@ -1143,12 +1139,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
} }
#[allow(trivial_numeric_casts)] #[allow(trivial_numeric_casts)]
fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) { fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id); let def = tcx.adt_def(def_id);
let sp = tcx.def_span(def_id);
def.destructor(tcx); // force the destructor to be evaluated def.destructor(tcx); // force the destructor to be evaluated
if vs.is_empty() { if def.variants().is_empty() {
if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() { if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() {
struct_span_err!( struct_span_err!(
tcx.sess, tcx.sess,
@ -1156,7 +1151,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
E0084, E0084,
"unsupported representation for zero-variant enum" "unsupported representation for zero-variant enum"
) )
.span_label(sp, "zero-variant enum") .span_label(tcx.def_span(def_id), "zero-variant enum")
.emit(); .emit();
} }
} }
@ -1167,88 +1162,96 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
feature_err( feature_err(
&tcx.sess.parse_sess, &tcx.sess.parse_sess,
sym::repr128, sym::repr128,
sp, tcx.def_span(def_id),
"repr with 128-bit type is unstable", "repr with 128-bit type is unstable",
) )
.emit(); .emit();
} }
} }
for v in vs { for v in def.variants() {
if let Some(ref e) = v.disr_expr { if let ty::VariantDiscr::Explicit(discr_def_id) = v.discr {
tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); tcx.ensure().typeck(discr_def_id.expect_local());
} }
} }
if tcx.adt_def(def_id).repr().int.is_none() { if def.repr().int.is_none() {
let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind, CtorKind::Const);
let has_disr = |var: &ty::VariantDef| matches!(var.discr, ty::VariantDiscr::Explicit(_));
let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); let has_non_units = def.variants().iter().any(|var| !is_unit(var));
let has_non_units = vs.iter().any(|var| !is_unit(var)); let disr_units = def.variants().iter().any(|var| is_unit(&var) && has_disr(&var));
let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); let disr_non_unit = def.variants().iter().any(|var| !is_unit(&var) && has_disr(&var));
let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
if disr_non_unit || (disr_units && has_non_units) { if disr_non_unit || (disr_units && has_non_units) {
let mut err = let mut err = struct_span_err!(
struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); tcx.sess,
tcx.def_span(def_id),
E0732,
"`#[repr(inttype)]` must be specified"
);
err.emit(); err.emit();
} }
} }
detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp); detect_discriminant_duplicate(tcx, def);
check_transparent(tcx, def);
check_transparent(tcx, sp, def);
} }
/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
fn detect_discriminant_duplicate<'tcx>( fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) {
tcx: TyCtxt<'tcx>,
mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
vs: &'tcx [hir::Variant<'tcx>],
self_span: Span,
) {
// Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate. // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate.
// Here `idx` refers to the order of which the discriminant appears, and its index in `vs` // Here `idx` refers to the order of which the discriminant appears, and its index in `vs`
let report = |dis: Discr<'tcx>, idx: usize, err: &mut Diagnostic| { let report = |dis: Discr<'tcx>, idx, err: &mut Diagnostic| {
let var = &vs[idx]; // HIR for the duplicate discriminant let var = adt.variant(idx); // HIR for the duplicate discriminant
let (span, display_discr) = match var.disr_expr { let (span, display_discr) = match var.discr {
Some(ref expr) => { ty::VariantDiscr::Explicit(discr_def_id) => {
// In the case the discriminant is both a duplicate and overflowed, let the user know // In the case the discriminant is both a duplicate and overflowed, let the user know
if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind if let hir::Node::AnonConst(expr) = tcx.hir().get_by_def_id(discr_def_id.expect_local())
&& let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
&& *lit_value != dis.val && *lit_value != dis.val
{ {
(tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)")) (tcx.def_span(discr_def_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
// Otherwise, format the value as-is
} else { } else {
(tcx.hir().span(expr.hir_id), format!("`{dis}`")) // Otherwise, format the value as-is
(tcx.def_span(discr_def_id), format!("`{dis}`"))
} }
} }
None => { // This should not happen.
ty::VariantDiscr::Relative(0) => (tcx.def_span(var.def_id), format!("`{dis}`")),
ty::VariantDiscr::Relative(distance_to_explicit) => {
// At this point we know this discriminant is a duplicate, and was not explicitly // At this point we know this discriminant is a duplicate, and was not explicitly
// assigned by the user. Here we iterate backwards to fetch the HIR for the last // assigned by the user. Here we iterate backwards to fetch the HIR for the last
// explicitly assigned discriminant, and letting the user know that this was the // explicitly assigned discriminant, and letting the user know that this was the
// increment startpoint, and how many steps from there leading to the duplicate // increment startpoint, and how many steps from there leading to the duplicate
if let Some((n, hir::Variant { span, ident, .. })) = if let Some(explicit_idx) =
vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some()) idx.as_u32().checked_sub(distance_to_explicit).map(VariantIdx::from_u32)
{ {
let ve_ident = var.ident; let explicit_variant = adt.variant(explicit_idx);
let n = n + 1; let ve_ident = var.name;
let sp = if n > 1 { "variants" } else { "variant" }; let ex_ident = explicit_variant.name;
let sp = if distance_to_explicit > 1 { "variants" } else { "variant" };
err.span_label( err.span_label(
*span, tcx.def_span(explicit_variant.def_id),
format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"), format!(
"discriminant for `{ve_ident}` incremented from this startpoint \
(`{ex_ident}` + {distance_to_explicit} {sp} later \
=> `{ve_ident}` = {dis})"
),
); );
} }
(vs[idx].span, format!("`{dis}`")) (tcx.def_span(var.def_id), format!("`{dis}`"))
} }
}; };
err.span_label(span, format!("{display_discr} assigned here")); err.span_label(span, format!("{display_discr} assigned here"));
}; };
let mut discrs = adt.discriminants(tcx).collect::<Vec<_>>();
// Here we loop through the discriminants, comparing each discriminant to another. // Here we loop through the discriminants, comparing each discriminant to another.
// When a duplicate is detected, we instantiate an error and point to both // When a duplicate is detected, we instantiate an error and point to both
// initial and duplicate value. The duplicate discriminant is then discarded by swapping // initial and duplicate value. The duplicate discriminant is then discarded by swapping
@ -1257,29 +1260,29 @@ fn detect_discriminant_duplicate<'tcx>(
// style as we are mutating `discrs` on the fly). // style as we are mutating `discrs` on the fly).
let mut i = 0; let mut i = 0;
while i < discrs.len() { while i < discrs.len() {
let hir_var_i_idx = discrs[i].0.index(); let var_i_idx = discrs[i].0;
let mut error: Option<DiagnosticBuilder<'_, _>> = None; let mut error: Option<DiagnosticBuilder<'_, _>> = None;
let mut o = i + 1; let mut o = i + 1;
while o < discrs.len() { while o < discrs.len() {
let hir_var_o_idx = discrs[o].0.index(); let var_o_idx = discrs[o].0;
if discrs[i].1.val == discrs[o].1.val { if discrs[i].1.val == discrs[o].1.val {
let err = error.get_or_insert_with(|| { let err = error.get_or_insert_with(|| {
let mut ret = struct_span_err!( let mut ret = struct_span_err!(
tcx.sess, tcx.sess,
self_span, tcx.def_span(adt.did()),
E0081, E0081,
"discriminant value `{}` assigned more than once", "discriminant value `{}` assigned more than once",
discrs[i].1, discrs[i].1,
); );
report(discrs[i].1, hir_var_i_idx, &mut ret); report(discrs[i].1, var_i_idx, &mut ret);
ret ret
}); });
report(discrs[o].1, hir_var_o_idx, err); report(discrs[o].1, var_o_idx, err);
// Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty
discrs[o] = *discrs.last().unwrap(); discrs[o] = *discrs.last().unwrap();

View file

@ -32,7 +32,7 @@ LL | First = -1,
| -- `-1` assigned here | -- `-1` assigned here
LL | LL |
LL | Second = -2, LL | Second = -2,
| ----------- discriminant for `Last` incremented from this startpoint (`Second` + 1 variant later => `Last` = -1) | ------ discriminant for `Last` incremented from this startpoint (`Second` + 1 variant later => `Last` = -1)
LL | LL |
LL | Last, LL | Last,
| ---- `-1` assigned here | ---- `-1` assigned here
@ -53,7 +53,7 @@ LL | V4 = 0,
| - `0` assigned here | - `0` assigned here
LL | LL |
LL | V5 = -2, LL | V5 = -2,
| ------- discriminant for `V7` incremented from this startpoint (`V5` + 2 variants later => `V7` = 0) | -- discriminant for `V7` incremented from this startpoint (`V5` + 2 variants later => `V7` = 0)
... ...
LL | V7, LL | V7,
| -- `0` assigned here | -- `0` assigned here
@ -68,7 +68,7 @@ LL | V5 = -2,
| -- `-2` assigned here | -- `-2` assigned here
... ...
LL | V8 = -3, LL | V8 = -3,
| ------- discriminant for `V9` incremented from this startpoint (`V8` + 1 variant later => `V9` = -2) | -- discriminant for `V9` incremented from this startpoint (`V8` + 1 variant later => `V9` = -2)
LL | LL |
LL | V9, LL | V9,
| -- `-2` assigned here | -- `-2` assigned here