1
Fork 0

Rollup merge of #123993 - compiler-errors:coroutine-obl, r=lcnr

Do `check_coroutine_obligations` once per typeck root

We only need to do `check_coroutine_obligations` once per typeck root, especially since the new solver can't really (easily) associate which obligations correspond to which coroutines.

This requires us to move the checks for sized coroutine fields into `mir_coroutine_witnesses`, but that's fine imo.

r? lcnr
This commit is contained in:
León Orell Valerian Liehr 2024-04-23 17:25:16 +02:00 committed by GitHub
commit e15d6f9d85
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 179 additions and 143 deletions

View file

@ -10,11 +10,10 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::Node;
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
use rustc_infer::traits::Obligation;
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
@ -24,10 +23,10 @@ use rustc_middle::ty::{
};
use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
use rustc_target::abi::FieldIdx;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _};
use rustc_type_ir::fold::TypeFoldable;
use std::cell::LazyCell;
@ -1715,55 +1714,31 @@ fn opaque_type_cycle_error(
err.emit()
}
// FIXME(@lcnr): This should not be computed per coroutine, but instead once for
// each typeck root.
pub(super) fn check_coroutine_obligations(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
debug_assert!(tcx.is_coroutine(def_id.to_def_id()));
debug_assert!(!tcx.is_typeck_child(def_id.to_def_id()));
let typeck = tcx.typeck(def_id);
let param_env = tcx.param_env(typeck.hir_owner.def_id);
let typeck_results = tcx.typeck(def_id);
let param_env = tcx.param_env(def_id);
let coroutine_interior_predicates = &typeck.coroutine_interior_predicates[&def_id];
debug!(?coroutine_interior_predicates);
debug!(?typeck_results.coroutine_stalled_predicates);
let infcx = tcx
.infer_ctxt()
// typeck writeback gives us predicates with their regions erased.
// As borrowck already has checked lifetimes, we do not need to do it again.
.ignoring_regions()
// Bind opaque types to type checking root, as they should have been checked by borrowck,
// but may show up in some cases, like when (root) obligations are stalled in the new solver.
.with_opaque_type_inference(typeck.hir_owner.def_id)
.with_opaque_type_inference(def_id)
.build();
let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(&infcx);
for (predicate, cause) in coroutine_interior_predicates {
let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate);
fulfillment_cx.register_predicate_obligation(&infcx, obligation);
let ocx = ObligationCtxt::new(&infcx);
for (predicate, cause) in &typeck_results.coroutine_stalled_predicates {
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, *predicate));
}
if (tcx.features().unsized_locals || tcx.features().unsized_fn_params)
&& let Some(coroutine) = tcx.mir_coroutine_witnesses(def_id)
{
for field_ty in coroutine.field_tys.iter() {
fulfillment_cx.register_bound(
&infcx,
param_env,
field_ty.ty,
tcx.require_lang_item(hir::LangItem::Sized, Some(field_ty.source_info.span)),
ObligationCause::new(
field_ty.source_info.span,
def_id,
ObligationCauseCode::SizedCoroutineInterior(def_id),
),
);
}
}
let errors = fulfillment_cx.select_all_or_error(&infcx);
let errors = ocx.select_all_or_error();
debug!(?errors);
if !errors.is_empty() {
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));

View file

@ -588,12 +588,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
obligations
.extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx));
let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)).collect();
debug!(?obligations);
self.typeck_results
.borrow_mut()
.coroutine_interior_predicates
.insert(expr_def_id, obligations);
let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause));
self.typeck_results.borrow_mut().coroutine_stalled_predicates.extend(obligations);
}
}

View file

@ -23,7 +23,7 @@ use rustc_hir::{
};
use rustc_hir_analysis::collect::suggest_impl_trait;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::traits::{self};
use rustc_infer::traits;
use rustc_middle::lint::in_external_macro;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::ty::print::with_no_trimmed_paths;

View file

@ -551,15 +551,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
fn visit_coroutine_interior(&mut self) {
let fcx_typeck_results = self.fcx.typeck_results.borrow();
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
self.tcx().with_stable_hashing_context(move |ref hcx| {
for (&expr_def_id, predicates) in
fcx_typeck_results.coroutine_interior_predicates.to_sorted(hcx, false).into_iter()
{
let predicates =
self.resolve(predicates.clone(), &self.fcx.tcx.def_span(expr_def_id));
self.typeck_results.coroutine_interior_predicates.insert(expr_def_id, predicates);
}
})
for (predicate, cause) in &fcx_typeck_results.coroutine_stalled_predicates {
let (predicate, cause) = self.resolve((*predicate, cause.clone()), &cause.span);
self.typeck_results.coroutine_stalled_predicates.insert((predicate, cause));
}
}
#[instrument(skip(self), level = "debug")]

View file

@ -759,7 +759,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
tcx.hir().par_body_owners(|def_id| {
if tcx.is_coroutine(def_id.to_def_id()) {
tcx.ensure().mir_coroutine_witnesses(def_id);
tcx.ensure().check_coroutine_obligations(def_id);
tcx.ensure().check_coroutine_obligations(
tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
);
}
});
sess.time("layout_testing", || layout_test::test_layout(tcx));

View file

@ -7,10 +7,8 @@ use crate::{
GenericArgs, GenericArgsRef, Ty, UserArgs,
},
};
use rustc_data_structures::{
fx::FxIndexMap,
unord::{ExtendUnord, UnordItems, UnordSet},
};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::{
self as hir,
@ -201,8 +199,7 @@ pub struct TypeckResults<'tcx> {
/// Stores the predicates that apply on coroutine witness types.
/// formatting modified file tests/ui/coroutine/retain-resume-ref.rs
pub coroutine_interior_predicates:
LocalDefIdMap<Vec<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>>,
pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>,
/// We sometimes treat byte string literals (which are of type `&[u8; N]`)
/// as `&[u8]`, depending on the pattern in which they are used.
@ -243,7 +240,7 @@ impl<'tcx> TypeckResults<'tcx> {
closure_min_captures: Default::default(),
closure_fake_reads: Default::default(),
rvalue_scopes: Default::default(),
coroutine_interior_predicates: Default::default(),
coroutine_stalled_predicates: Default::default(),
treat_byte_string_as_slice: Default::default(),
closure_size_eval: Default::default(),
offset_of_data: Default::default(),

View file

@ -80,6 +80,10 @@ use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::abi::{FieldIdx, VariantIdx};
use rustc_target::spec::PanicStrategy;
use rustc_trait_selection::infer::TyCtxtInferExt as _;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
use std::{iter, ops};
pub struct StateTransform;
@ -1584,10 +1588,46 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>(
let (_, coroutine_layout, _) = compute_layout(liveness_info, body);
check_suspend_tys(tcx, &coroutine_layout, body);
check_field_tys_sized(tcx, &coroutine_layout, def_id);
Some(coroutine_layout)
}
fn check_field_tys_sized<'tcx>(
tcx: TyCtxt<'tcx>,
coroutine_layout: &CoroutineLayout<'tcx>,
def_id: LocalDefId,
) {
// No need to check if unsized_locals/unsized_fn_params is disabled,
// since we will error during typeck.
if !tcx.features().unsized_locals && !tcx.features().unsized_fn_params {
return;
}
let infcx = tcx.infer_ctxt().ignoring_regions().build();
let param_env = tcx.param_env(def_id);
let ocx = ObligationCtxt::new(&infcx);
for field_ty in &coroutine_layout.field_tys {
ocx.register_bound(
ObligationCause::new(
field_ty.source_info.span,
def_id,
ObligationCauseCode::SizedCoroutineInterior(def_id),
),
param_env,
field_ty.ty,
tcx.require_lang_item(hir::LangItem::Sized, Some(field_ty.source_info.span)),
);
}
let errors = ocx.select_all_or_error();
debug!(?errors);
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(errors);
}
}
impl<'tcx> MirPass<'tcx> for StateTransform {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let Some(old_yield_ty) = body.yield_ty() else {

View file

@ -14,7 +14,7 @@ use rustc_data_structures::sync::{IntoDynSyncSend, MappedReadGuard, ReadGuard, R
use rustc_data_structures::unhash::UnhashMap;
use std::fs;
use std::io::{self, BorrowedBuf, Read};
use std::path::{self};
use std::path;
#[cfg(test)]
mod tests;