merge PlaceTy field_ty computation

This commit is contained in:
lcnr 2024-12-18 14:02:42 +01:00
parent 809f55d66a
commit 51cd03a127
2 changed files with 65 additions and 123 deletions

View file

@ -3,7 +3,7 @@
use std::rc::Rc;
use std::{fmt, iter, mem};
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_abi::FieldIdx;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::ErrorGuaranteed;
@ -273,35 +273,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
| ProjectionElem::Downcast(..) => {}
ProjectionElem::Field(field, fty) => {
let fty = self.typeck.normalize(fty, location);
match self.expected_field_ty(base_ty, field, location) {
Ok(ty) => {
let ty = self.typeck.normalize(ty, location);
debug!(?fty, ?ty);
let ty = base_ty.field_ty(tcx, field);
let ty = self.typeck.normalize(ty, location);
debug!(?fty, ?ty);
if let Err(terr) = self.typeck.relate_types(
ty,
context.ambient_variance(),
fty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
self,
place,
"bad field access ({:?}: {:?}): {:?}",
ty,
fty,
terr
);
}
}
Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!(
self,
place,
"accessed field #{} but variant only has {}",
field.index(),
field_count
),
if let Err(terr) = self.typeck.relate_types(
ty,
context.ambient_variance(),
fty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(self, place, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr);
}
}
ProjectionElem::OpaqueCast(ty) => {
@ -589,82 +572,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
self.typeck.constraints.liveness_constraints.add_location(region, location);
}
}
fn expected_field_ty(
&mut self,
base_ty: PlaceTy<'tcx>,
field: FieldIdx,
location: Location,
) -> Result<Ty<'tcx>, FieldAccessError> {
let tcx = self.tcx();
let (variant, args) = match base_ty {
PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() {
ty::Adt(adt_def, args) => (adt_def.variant(variant_index), args),
ty::Coroutine(def_id, args) => {
let mut variants = args.as_coroutine().state_tys(def_id, tcx);
let Some(mut variant) = variants.nth(variant_index.into()) else {
bug!(
"variant_index of coroutine out of range: {:?}/{:?}",
variant_index,
args.as_coroutine().state_tys(def_id, tcx).count()
);
};
return match variant.nth(field.index()) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange { field_count: variant.count() }),
};
}
_ => bug!("can't have downcast of non-adt non-coroutine type"),
},
PlaceTy { ty, variant_index: None } => match *ty.kind() {
ty::Adt(adt_def, args) if !adt_def.is_enum() => {
(adt_def.variant(FIRST_VARIANT), args)
}
ty::Closure(_, args) => {
return match args.as_closure().upvar_tys().get(field.index()) {
Some(&ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_closure().upvar_tys().len(),
}),
};
}
ty::CoroutineClosure(_, args) => {
return match args.as_coroutine_closure().upvar_tys().get(field.index()) {
Some(&ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine_closure().upvar_tys().len(),
}),
};
}
ty::Coroutine(_, args) => {
// Only prefix fields (upvars and current state) are
// accessible without a variant index.
return match args.as_coroutine().prefix_tys().get(field.index()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine().prefix_tys().len(),
}),
};
}
ty::Tuple(tys) => {
return match tys.get(field.index()) {
Some(&ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange { field_count: tys.len() }),
};
}
_ => {
span_bug!(self.last_span, "can't project out of {:?}", base_ty);
}
},
};
if let Some(field) = variant.fields.get(field) {
Ok(self.typeck.normalize(field.ty(tcx, args), location))
} else {
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
}
}
}
/// The MIR type checker. Visits the MIR and enforces all the

View file

@ -5,6 +5,7 @@
use rustc_hir as hir;
use tracing::{debug, instrument};
use ty::CoroutineArgsExt;
use crate::mir::*;
@ -25,29 +26,63 @@ impl<'tcx> PlaceTy<'tcx> {
PlaceTy { ty, variant_index: None }
}
/// `place_ty.field_ty(tcx, f)` computes the type at a given field
/// of a record or enum-variant. (Most clients of `PlaceTy` can
/// instead just extract the relevant type directly from their
/// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do
/// not carry a `Ty` for `T`.)
/// `place_ty.field_ty(tcx, f)` computes the type of a given field.
///
/// Most clients of `PlaceTy` can instead just extract the relevant type
/// directly from their `PlaceElem`, but some instances of `ProjectionElem<V, T>`
/// do not carry a `Ty` for `T`.
///
/// Note that the resulting type has not been normalized.
#[instrument(level = "debug", skip(tcx), ret)]
pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> {
match self.ty.kind() {
ty::Adt(adt_def, args) => {
let variant_def = match self.variant_index {
None => adt_def.non_enum_variant(),
Some(variant_index) => {
assert!(adt_def.is_enum());
adt_def.variant(variant_index)
}
};
let field_def = &variant_def.fields[f];
field_def.ty(tcx, args)
if let Some(variant_index) = self.variant_index {
match *self.ty.kind() {
ty::Adt(adt_def, args) if adt_def.is_enum() => {
adt_def.variant(variant_index).fields[f].ty(tcx, args)
}
ty::Coroutine(def_id, args) => {
let mut variants = args.as_coroutine().state_tys(def_id, tcx);
let Some(mut variant) = variants.nth(variant_index.into()) else {
bug!("variant {variant_index:?} of coroutine out of range: {self:?}");
};
variant
.nth(f.index())
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}"))
}
_ => bug!("can't downcast non-adt non-coroutine type: {self:?}"),
}
} else {
match self.ty.kind() {
ty::Adt(adt_def, args) if !adt_def.is_enum() => {
adt_def.non_enum_variant().fields[f].ty(tcx, args)
}
ty::Closure(_, args) => args
.as_closure()
.upvar_tys()
.get(f.index())
.copied()
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
ty::CoroutineClosure(_, args) => args
.as_coroutine_closure()
.upvar_tys()
.get(f.index())
.copied()
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
// Only prefix fields (upvars and current state) are
// accessible without a variant index.
ty::Coroutine(_, args) => args
.as_coroutine()
.prefix_tys()
.get(f.index())
.copied()
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
ty::Tuple(tys) => tys
.get(f.index())
.copied()
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
_ => bug!("can't project out of {self:?}"),
}
ty::Tuple(tys) => tys[f.index()],
_ => bug!("extracting field of non-tuple non-adt: {:?}", self),
}
}