1
Fork 0

Eagerly detect coroutine recursion pre-mono when possible

This commit is contained in:
Michael Goulet 2025-01-27 19:15:37 +00:00
parent 820bfffc25
commit d0b0b028a6
12 changed files with 37 additions and 95 deletions

View file

@ -19,7 +19,7 @@ use rustc_middle::span_bug;
use rustc_middle::ty::error::TypeErrorToStringExt;
use rustc_middle::ty::fold::{BottomUpFolder, fold_regions};
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{
AdtDef, GenericArgKind, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
};
@ -257,30 +257,9 @@ pub(super) fn check_opaque_for_cycles<'tcx>(
// First, try to look at any opaque expansion cycles, considering coroutine fields
// (even though these aren't necessarily true errors).
if tcx
.try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::Yes)
.is_err()
{
// Look for true opaque expansion cycles, but ignore coroutines.
// This will give us any true errors. Coroutines are only problematic
// if they cause layout computation errors.
if tcx
.try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No)
.is_err()
{
let reported = opaque_type_cycle_error(tcx, def_id);
return Err(reported);
}
// And also look for cycle errors in the layout of coroutines.
if let Err(&LayoutError::Cycle(guar)) =
tcx.layout_of(
ty::TypingEnv::post_analysis(tcx, def_id.to_def_id())
.as_query_input(Ty::new_opaque(tcx, def_id.to_def_id(), args)),
)
{
return Err(guar);
}
if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() {
let reported = opaque_type_cycle_error(tcx, def_id);
return Err(reported);
}
Ok(())

View file

@ -909,6 +909,11 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
tcx.ensure_ok().check_coroutine_obligations(
tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
);
// Eagerly check the unsubstituted layout for cycles.
tcx.ensure_ok().layout_of(
ty::TypingEnv::post_analysis(tcx, def_id.to_def_id())
.as_query_input(tcx.type_of(def_id).instantiate_identity()),
);
}
});
});

View file

@ -777,7 +777,6 @@ impl<'tcx> TyCtxt<'tcx> {
self,
def_id: DefId,
args: GenericArgsRef<'tcx>,
inspect_coroutine_fields: InspectCoroutineFields,
) -> Result<Ty<'tcx>, Ty<'tcx>> {
let mut visitor = OpaqueTypeExpander {
seen_opaque_tys: FxHashSet::default(),
@ -786,9 +785,7 @@ impl<'tcx> TyCtxt<'tcx> {
found_recursion: false,
found_any_recursion: false,
check_recursion: true,
expand_coroutines: true,
tcx: self,
inspect_coroutine_fields,
};
let expanded_type = visitor.expand_opaque_ty(def_id, args).unwrap();
@ -965,19 +962,11 @@ struct OpaqueTypeExpander<'tcx> {
primary_def_id: Option<DefId>,
found_recursion: bool,
found_any_recursion: bool,
expand_coroutines: bool,
/// Whether or not to check for recursive opaque types.
/// This is `true` when we're explicitly checking for opaque type
/// recursion, and 'false' otherwise to avoid unnecessary work.
check_recursion: bool,
tcx: TyCtxt<'tcx>,
inspect_coroutine_fields: InspectCoroutineFields,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum InspectCoroutineFields {
No,
Yes,
}
impl<'tcx> OpaqueTypeExpander<'tcx> {
@ -1009,41 +998,6 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
None
}
}
fn expand_coroutine(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) -> Option<Ty<'tcx>> {
if self.found_any_recursion {
return None;
}
let args = args.fold_with(self);
if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
let expanded_ty = match self.expanded_cache.get(&(def_id, args)) {
Some(expanded_ty) => *expanded_ty,
None => {
if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) {
for bty in self.tcx.bound_coroutine_hidden_types(def_id) {
let hidden_ty = self.tcx.instantiate_bound_regions_with_erased(
bty.instantiate(self.tcx, args),
);
self.fold_ty(hidden_ty);
}
}
let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
self.expanded_cache.insert((def_id, args), expanded_ty);
expanded_ty
}
};
if self.check_recursion {
self.seen_opaque_tys.remove(&def_id);
}
Some(expanded_ty)
} else {
// If another opaque type that we contain is recursive, then it
// will report the error, so we don't have to.
self.found_any_recursion = true;
self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap();
None
}
}
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
@ -1052,19 +1006,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
let mut t = if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() {
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() {
self.expand_opaque_ty(def_id, args).unwrap_or(t)
} else if t.has_opaque_types() || t.has_coroutines() {
} else if t.has_opaque_types() {
t.super_fold_with(self)
} else {
t
};
if self.expand_coroutines {
if let ty::CoroutineWitness(def_id, args) = *t.kind() {
t = self.expand_coroutine(def_id, args).unwrap_or(t);
}
}
t
}
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
@ -1753,9 +1701,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
found_recursion: false,
found_any_recursion: false,
check_recursion: false,
expand_coroutines: false,
tcx,
inspect_coroutine_fields: InspectCoroutineFields::No,
};
val.fold_with(&mut visitor)
}