Rollup merge of #103759 - cjgillot:adt-collect, r=davidtwco

Use `adt_def` during type collection.

This removes a wrapper which is close to what `adt_def` does.
This commit is contained in:
Dylan DPC 2022-11-01 14:12:27 +05:30 committed by GitHub
commit 09f4f7c8f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 164 deletions

View file

@ -6,7 +6,7 @@ use super::*;
use rustc_attr as attr;
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
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::intravisit::Visitor;
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_transparent(tcx, span, def);
check_transparent(tcx, 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 span = tcx.def_span(def_id);
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_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);
}
DefKind::Enum => {
let item = tcx.hir().item(id);
let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
return;
};
check_enum(tcx, &enum_definition.variants, item.owner_id.def_id);
check_enum(tcx, id.owner_id.def_id);
}
DefKind::Fn => {} // entirely within check_item_body
DefKind::Impl => {
@ -1026,7 +1022,7 @@ pub(super) fn check_packed_inner(
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() {
return;
}
@ -1035,14 +1031,14 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
feature_err(
&tcx.sess.parse_sess,
sym::transparent_unions,
sp,
tcx.def_span(adt.did()),
"transparent unions are unstable",
)
.emit();
}
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() {
// Don't bother checking the fields. No variants (and thus no fields) exist.
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 });
let non_zst_count = non_zst_fields.clone().count();
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 =
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)]
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 sp = tcx.def_span(def_id);
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() {
struct_span_err!(
tcx.sess,
@ -1156,7 +1151,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
E0084,
"unsupported representation for zero-variant enum"
)
.span_label(sp, "zero-variant enum")
.span_label(tcx.def_span(def_id), "zero-variant enum")
.emit();
}
}
@ -1167,88 +1162,96 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
feature_err(
&tcx.sess.parse_sess,
sym::repr128,
sp,
tcx.def_span(def_id),
"repr with 128-bit type is unstable",
)
.emit();
}
}
for v in vs {
if let Some(ref e) = v.disr_expr {
tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id));
for v in def.variants() {
if let ty::VariantDiscr::Explicit(discr_def_id) = v.discr {
tcx.ensure().typeck(discr_def_id.expect_local());
}
}
if tcx.adt_def(def_id).repr().int.is_none() {
let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..));
if def.repr().int.is_none() {
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 = vs.iter().any(|var| !is_unit(var));
let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
let has_non_units = def.variants().iter().any(|var| !is_unit(var));
let disr_units = def.variants().iter().any(|var| is_unit(&var) && has_disr(&var));
let disr_non_unit = def.variants().iter().any(|var| !is_unit(&var) && has_disr(&var));
if disr_non_unit || (disr_units && has_non_units) {
let mut err =
struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified");
let mut err = struct_span_err!(
tcx.sess,
tcx.def_span(def_id),
E0732,
"`#[repr(inttype)]` must be specified"
);
err.emit();
}
}
detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
check_transparent(tcx, sp, def);
detect_discriminant_duplicate(tcx, def);
check_transparent(tcx, def);
}
/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
fn detect_discriminant_duplicate<'tcx>(
tcx: TyCtxt<'tcx>,
mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
vs: &'tcx [hir::Variant<'tcx>],
self_span: Span,
) {
fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) {
// 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`
let report = |dis: Discr<'tcx>, idx: usize, err: &mut Diagnostic| {
let var = &vs[idx]; // HIR for the duplicate discriminant
let (span, display_discr) = match var.disr_expr {
Some(ref expr) => {
let report = |dis: Discr<'tcx>, idx, err: &mut Diagnostic| {
let var = adt.variant(idx); // HIR for the duplicate discriminant
let (span, display_discr) = match var.discr {
ty::VariantDiscr::Explicit(discr_def_id) => {
// 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
&& *lit_value != dis.val
{
(tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
// Otherwise, format the value as-is
(tcx.def_span(discr_def_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
} 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
// 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
// increment startpoint, and how many steps from there leading to the duplicate
if let Some((n, hir::Variant { span, ident, .. })) =
vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
if let Some(explicit_idx) =
idx.as_u32().checked_sub(distance_to_explicit).map(VariantIdx::from_u32)
{
let ve_ident = var.ident;
let n = n + 1;
let sp = if n > 1 { "variants" } else { "variant" };
let explicit_variant = adt.variant(explicit_idx);
let ve_ident = var.name;
let ex_ident = explicit_variant.name;
let sp = if distance_to_explicit > 1 { "variants" } else { "variant" };
err.span_label(
*span,
format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"),
tcx.def_span(explicit_variant.def_id),
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"));
};
let mut discrs = adt.discriminants(tcx).collect::<Vec<_>>();
// Here we loop through the discriminants, comparing each discriminant to another.
// 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
@ -1257,29 +1260,29 @@ fn detect_discriminant_duplicate<'tcx>(
// style as we are mutating `discrs` on the fly).
let mut i = 0;
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 o = i + 1;
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 {
let err = error.get_or_insert_with(|| {
let mut ret = struct_span_err!(
tcx.sess,
self_span,
tcx.def_span(adt.did()),
E0081,
"discriminant value `{}` assigned more than once",
discrs[i].1,
);
report(discrs[i].1, hir_var_i_idx, &mut ret);
report(discrs[i].1, var_i_idx, &mut 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
discrs[o] = *discrs.last().unwrap();

View file

@ -218,19 +218,16 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
hir::ItemKind::Const(ty, ..) => {
check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Struct(ref struct_def, ref ast_generics) => {
check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
hir::ItemKind::Struct(_, ref ast_generics) => {
check_type_defn(tcx, item, false);
check_variances_for_type_defn(tcx, item, ast_generics);
}
hir::ItemKind::Union(ref struct_def, ref ast_generics) => {
check_type_defn(tcx, item, true, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
hir::ItemKind::Union(_, ref ast_generics) => {
check_type_defn(tcx, item, true);
check_variances_for_type_defn(tcx, item, ast_generics);
}
hir::ItemKind::Enum(ref enum_def, ref ast_generics) => {
check_type_defn(tcx, item, true, |wfcx| wfcx.enum_variants(enum_def));
hir::ItemKind::Enum(_, ref ast_generics) => {
check_type_defn(tcx, item, true);
check_variances_for_type_defn(tcx, item, ast_generics);
}
hir::ItemKind::Trait(..) => {
@ -1037,27 +1034,25 @@ fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> {
}
/// In a type definition, we check that to ensure that the types of the fields are well-formed.
fn check_type_defn<'tcx, F>(
tcx: TyCtxt<'tcx>,
item: &hir::Item<'tcx>,
all_sized: bool,
mut lookup_fields: F,
) where
F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
{
fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: bool) {
let _ = tcx.representability(item.owner_id.def_id);
let adt_def = tcx.adt_def(item.owner_id);
enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
let variants = lookup_fields(wfcx);
let packed = tcx.adt_def(item.owner_id).repr().packed();
let variants = adt_def.variants();
let packed = adt_def.repr().packed();
for variant in &variants {
for variant in variants.iter() {
// All field types must be well-formed.
for field in &variant.fields {
let field_id = field.did.expect_local();
let hir::Node::Field(hir::FieldDef { ty: hir_ty, .. }) = tcx.hir().get_by_def_id(field_id)
else { bug!() };
let ty = wfcx.normalize(hir_ty.span, None, tcx.type_of(field.did));
wfcx.register_wf_obligation(
field.span,
Some(WellFormedLoc::Ty(field.def_id)),
field.ty.into(),
hir_ty.span,
Some(WellFormedLoc::Ty(field_id)),
ty.into(),
)
}
@ -1065,7 +1060,7 @@ fn check_type_defn<'tcx, F>(
// intermediate types must be sized.
let needs_drop_copy = || {
packed && {
let ty = variant.fields.last().unwrap().ty;
let ty = tcx.type_of(variant.fields.last().unwrap().did);
let ty = tcx.erase_regions(ty);
if ty.needs_infer() {
tcx.sess
@ -1084,27 +1079,31 @@ fn check_type_defn<'tcx, F>(
variant.fields[..variant.fields.len() - unsized_len].iter().enumerate()
{
let last = idx == variant.fields.len() - 1;
let field_id = field.did.expect_local();
let hir::Node::Field(hir::FieldDef { ty: hir_ty, .. }) = tcx.hir().get_by_def_id(field_id)
else { bug!() };
let ty = wfcx.normalize(hir_ty.span, None, tcx.type_of(field.did));
wfcx.register_bound(
traits::ObligationCause::new(
field.span,
hir_ty.span,
wfcx.body_id,
traits::FieldSized {
adt_kind: match item_adt_kind(&item.kind) {
Some(i) => i,
None => bug!(),
},
span: field.span,
span: hir_ty.span,
last,
},
),
wfcx.param_env,
field.ty,
ty,
tcx.require_lang_item(LangItem::Sized, None),
);
}
// Explicit `enum` discriminant values must const-evaluate successfully.
if let Some(discr_def_id) = variant.explicit_discr {
if let ty::VariantDiscr::Explicit(discr_def_id) = variant.discr {
let cause = traits::ObligationCause::new(
tcx.def_span(discr_def_id),
wfcx.body_id,
@ -1114,7 +1113,7 @@ fn check_type_defn<'tcx, F>(
cause,
wfcx.param_env,
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(
ty::Const::from_anon_const(tcx, discr_def_id),
ty::Const::from_anon_const(tcx, discr_def_id.expect_local()),
))
.to_predicate(tcx),
));
@ -1925,56 +1924,6 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) {
items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id));
}
///////////////////////////////////////////////////////////////////////////
// ADT
// FIXME(eddyb) replace this with getting fields/discriminants through `ty::AdtDef`.
struct AdtVariant<'tcx> {
/// Types of fields in the variant, that must be well-formed.
fields: Vec<AdtField<'tcx>>,
/// Explicit discriminant of this variant (e.g. `A = 123`),
/// that must evaluate to a constant value.
explicit_discr: Option<LocalDefId>,
}
struct AdtField<'tcx> {
ty: Ty<'tcx>,
def_id: LocalDefId,
span: Span,
}
impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> {
// FIXME(eddyb) replace this with getting fields through `ty::AdtDef`.
fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> {
let fields = struct_def
.fields()
.iter()
.map(|field| {
let def_id = self.tcx().hir().local_def_id(field.hir_id);
let field_ty = self.tcx().type_of(def_id);
let field_ty = self.normalize(field.ty.span, None, field_ty);
debug!("non_enum_variant: type of field {:?} is {:?}", field, field_ty);
AdtField { ty: field_ty, span: field.ty.span, def_id }
})
.collect();
AdtVariant { fields, explicit_discr: None }
}
fn enum_variants(&self, enum_def: &hir::EnumDef<'_>) -> Vec<AdtVariant<'tcx>> {
enum_def
.variants
.iter()
.map(|variant| AdtVariant {
fields: self.non_enum_variant(&variant.data).fields,
explicit_discr: variant
.disr_expr
.map(|explicit_discr| self.tcx().hir().local_def_id(explicit_discr.hir_id)),
})
.collect()
}
}
fn error_392(
tcx: TyCtxt<'_>,
span: Span,

View file

@ -604,11 +604,11 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
}
}
}
hir::ItemKind::Enum(ref enum_definition, _) => {
hir::ItemKind::Enum(..) => {
tcx.ensure().generics_of(def_id);
tcx.ensure().type_of(def_id);
tcx.ensure().predicates_of(def_id);
convert_enum_variant_types(tcx, def_id.to_def_id(), enum_definition.variants);
convert_enum_variant_types(tcx, def_id.to_def_id());
}
hir::ItemKind::Impl { .. } => {
tcx.ensure().generics_of(def_id);
@ -640,7 +640,8 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
}
if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
convert_variant_ctor(tcx, ctor_hir_id);
let ctor_def_id = tcx.hir().local_def_id(ctor_hir_id);
convert_variant_ctor(tcx, ctor_def_id);
}
}
@ -750,37 +751,34 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
}
}
fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) {
let def_id = tcx.hir().local_def_id(ctor_id);
fn convert_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {
tcx.ensure().generics_of(def_id);
tcx.ensure().type_of(def_id);
tcx.ensure().predicates_of(def_id);
}
fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) {
fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
let def = tcx.adt_def(def_id);
let repr_type = def.repr().discr_type();
let initial = repr_type.initial_discriminant(tcx);
let mut prev_discr = None::<Discr<'_>>;
// fill the discriminant values and field types
for variant in variants {
for variant in def.variants() {
let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx));
prev_discr = Some(
if let Some(ref e) = variant.disr_expr {
let expr_did = tcx.hir().local_def_id(e.hir_id);
def.eval_explicit_discr(tcx, expr_did.to_def_id())
if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr {
def.eval_explicit_discr(tcx, const_def_id)
} else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
Some(discr)
} else {
struct_span_err!(tcx.sess, variant.span, E0370, "enum discriminant overflowed")
.span_label(
variant.span,
format!("overflowed on value after {}", prev_discr.unwrap()),
)
let span = tcx.def_span(variant.def_id);
struct_span_err!(tcx.sess, span, E0370, "enum discriminant overflowed")
.span_label(span, format!("overflowed on value after {}", prev_discr.unwrap()))
.note(&format!(
"explicitly set `{} = {}` if that is desired outcome",
variant.ident, wrapped_discr
tcx.item_name(variant.def_id),
wrapped_discr
))
.emit();
None
@ -788,17 +786,16 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::V
.unwrap_or(wrapped_discr),
);
for f in variant.data.fields() {
let def_id = tcx.hir().local_def_id(f.hir_id);
tcx.ensure().generics_of(def_id);
tcx.ensure().type_of(def_id);
tcx.ensure().predicates_of(def_id);
for f in &variant.fields {
tcx.ensure().generics_of(f.did);
tcx.ensure().type_of(f.did);
tcx.ensure().predicates_of(f.did);
}
// Convert the ctor, if any. This also registers the variant as
// an item.
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
convert_variant_ctor(tcx, ctor_hir_id);
if let Some(ctor_def_id) = variant.ctor_def_id {
convert_variant_ctor(tcx, ctor_def_id.expect_local());
}
}
}

View file

@ -32,7 +32,7 @@ LL | First = -1,
| -- `-1` assigned here
LL |
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 | Last,
| ---- `-1` assigned here
@ -53,7 +53,7 @@ LL | V4 = 0,
| - `0` assigned here
LL |
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,
| -- `0` assigned here
@ -68,7 +68,7 @@ LL | V5 = -2,
| -- `-2` assigned here
...
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 | V9,
| -- `-2` assigned here