Rollup merge of #153581 - nnethercote:rm-cycle_stash, r=oli-obk

Simplify `type_of_opaque`.

There is a bunch of complexity supporting the "cannot check whether the hidden type of opaque type satisfies auto traits" error that shows up in `tests/ui/impl-trait/auto-trait-leak.rs`. This is an obscure error that shows up in a single test. If we are willing to downgrade that error message to a cycle error, we can do the following.

- Simplify the `type_of_opaque` return value.
- Remove the `cycle_stash` query modifier.
- Remove the `CyclePlaceholder` type.
- Remove the `SelectionError::OpaqueTypeAutoTraitLeakageUnknown` variant.
- Remove a `FromCycleError` impl.
- Remove `report_opaque_type_auto_trait_leakage`.
- Remove the `StashKey::Cycle` variant.
- Remove the `CycleErrorHandling::Stash` variant.

That's a lot! I think this is a worthwhile trade-off.

r? @oli-obk
This commit is contained in:
Jonathan Brouwer
2026-03-11 22:05:43 +01:00
committed by GitHub
16 changed files with 92 additions and 145 deletions
-2
View File
@@ -371,8 +371,6 @@ pub enum StashKey {
MaybeFruTypo,
CallAssocMethod,
AssociatedTypeSuggestion,
/// Query cycle detected, stashing in favor of a better error.
Cycle,
UndeterminedMacroResolution,
/// Used by `Parser::maybe_recover_trailing_expr`
ExprInPat,
@@ -4,7 +4,6 @@
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::VisitorExt;
use rustc_hir::{self as hir, AmbigArg, HirId};
use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, DefiningScopeKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
@@ -183,10 +182,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
}
},
Node::OpaqueTy(..) => tcx.type_of_opaque(def_id).map_or_else(
|CyclePlaceholder(guar)| Ty::new_error(tcx, guar),
|ty| ty.instantiate_identity(),
),
Node::OpaqueTy(..) => tcx.type_of_opaque(def_id).instantiate_identity(),
Node::ForeignItem(foreign_item) => match foreign_item.kind {
ForeignItemKind::Fn(..) => {
@@ -249,12 +245,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
}
}
pub(super) fn type_of_opaque(
tcx: TyCtxt<'_>,
def_id: DefId,
) -> Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
pub(super) fn type_of_opaque(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, Ty<'_>> {
if let Some(def_id) = def_id.as_local() {
Ok(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
opaque::find_opaque_ty_constraints_for_tait(
tcx,
@@ -287,11 +280,11 @@ pub(super) fn type_of_opaque(
DefiningScopeKind::MirBorrowck,
)
}
})
}
} else {
// Foreign opaque type will go through the foreign provider
// and load the type from metadata.
Ok(tcx.type_of(def_id))
tcx.type_of(def_id)
}
}
-9
View File
@@ -144,7 +144,6 @@ struct QueryModifiers {
arena_cache: Option<Ident>,
cache_on_disk_if: Option<CacheOnDiskIf>,
cycle_delay_bug: Option<Ident>,
cycle_stash: Option<Ident>,
depth_limit: Option<Ident>,
desc: Desc,
eval_always: Option<Ident>,
@@ -159,7 +158,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
let mut cache_on_disk_if = None;
let mut desc = None;
let mut cycle_delay_bug = None;
let mut cycle_stash = None;
let mut no_hash = None;
let mut anon = None;
let mut eval_always = None;
@@ -195,8 +193,6 @@ macro_rules! try_insert {
try_insert!(arena_cache = modifier);
} else if modifier == "cycle_delay_bug" {
try_insert!(cycle_delay_bug = modifier);
} else if modifier == "cycle_stash" {
try_insert!(cycle_stash = modifier);
} else if modifier == "no_hash" {
try_insert!(no_hash = modifier);
} else if modifier == "anon" {
@@ -221,7 +217,6 @@ macro_rules! try_insert {
cache_on_disk_if,
desc,
cycle_delay_bug,
cycle_stash,
no_hash,
anon,
eval_always,
@@ -257,7 +252,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream {
arena_cache,
cache_on_disk_if,
cycle_delay_bug,
cycle_stash,
depth_limit,
desc: _,
eval_always,
@@ -273,8 +267,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream {
let cycle_error_handling = if cycle_delay_bug.is_some() {
quote! { DelayBug }
} else if cycle_stash.is_some() {
quote! { Stash }
} else {
quote! { Error }
};
@@ -411,7 +403,6 @@ macro_rules! doc_link {
doc_link!(
arena_cache,
cycle_delay_bug,
cycle_stash,
no_hash,
anon,
eval_always,
+2 -10
View File
@@ -33,7 +33,6 @@
//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to
//! true. The query key identifier is available for use within the block, as is `tcx`.
//! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately.
//! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling.
//! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed.
//! - `anon`: Make the query anonymous in the dependency graph (no dep node is created).
//! - `eval_always`: Always evaluate the query, ignoring its dependencies and cached results.
@@ -118,7 +117,6 @@
CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono,
};
use crate::query::describe_as_module;
use crate::query::plumbing::CyclePlaceholder;
use crate::traits::query::{
CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal,
CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal,
@@ -339,22 +337,16 @@
feedable
}
/// Returns the *hidden type* of the opaque type given by `DefId` unless a cycle occurred.
///
/// This is a specialized instance of [`Self::type_of`] that detects query cycles.
/// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead.
/// This is used to improve the error message in cases where revealing the hidden type
/// for auto-trait leakage cycles.
/// Returns the *hidden type* of the opaque type given by `DefId`.
///
/// # Panics
///
/// This query will panic if the given definition is not an opaque type.
query type_of_opaque(key: DefId) -> Result<ty::EarlyBinder<'tcx, Ty<'tcx>>, CyclePlaceholder> {
query type_of_opaque(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
desc {
"computing type of opaque `{path}`",
path = tcx.def_path_str(key),
}
cycle_stash
}
query type_of_opaque_hir_typeck(key: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
desc {
-5
View File
@@ -14,7 +14,6 @@
use crate::mir::interpret::EvalToValTreeResult;
use crate::mir::mono::{MonoItem, NormalizationErrorInMono};
use crate::query::plumbing::CyclePlaceholder;
use crate::traits::solve;
use crate::ty::adjustment::CoerceUnsizedInfo;
use crate::ty::{self, Ty, TyCtxt};
@@ -212,10 +211,6 @@ impl Erasable for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
[u8; size_of::<Result<&'static ty::List<Ty<'static>>, ty::util::AlwaysRequiresDrop>>()];
}
impl Erasable for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
type Storage = [u8; size_of::<Result<ty::EarlyBinder<'static, Ty<'_>>, CyclePlaceholder>>()];
}
impl Erasable
for Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>
{
@@ -28,11 +28,6 @@
/// A cycle error results in a delay_bug call
pub(crate) struct cycle_delay_bug;
/// # `cycle_stash` query modifier
///
/// A cycle error results in a stashed cycle error that can be unstashed and canceled later
pub(crate) struct cycle_stash;
/// # `depth_limit` query modifier
///
/// Whether the query has a call depth limit
@@ -7,7 +7,6 @@
use rustc_data_structures::sync::{AtomicU64, WorkerLocal};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::hir_id::OwnerId;
use rustc_macros::HashStable;
use rustc_span::{ErrorGuaranteed, Span};
pub use sealed::IntoQueryParam;
@@ -58,7 +57,6 @@ pub enum ActiveKeyStatus<'tcx> {
pub enum CycleErrorHandling {
Error,
DelayBug,
Stash,
}
#[derive(Clone, Debug)]
@@ -651,9 +649,6 @@ fn into_query_param(self) -> LocalDefId {
}
}
#[derive(Copy, Clone, Debug, HashStable)]
pub struct CyclePlaceholder(pub ErrorGuaranteed);
#[cold]
pub(crate) fn default_query(name: &str, key: &dyn std::fmt::Debug) -> ! {
bug!(
-4
View File
@@ -633,10 +633,6 @@ pub enum SelectionError<'tcx> {
NotConstEvaluatable(NotConstEvaluatable),
/// Exceeded the recursion depth during type projection.
Overflow(OverflowError),
/// Computing an opaque type's hidden type caused an error (e.g. a cycle error).
/// We can thus not know whether the hidden type implements an auto trait, so
/// we should not presume anything about it.
OpaqueTypeAutoTraitLeakageUnknown(DefId),
/// Error for a `ConstArgHasType` goal
ConstArgHasWrongType { ct: ty::Const<'tcx>, ct_ty: Ty<'tcx>, expected_ty: Ty<'tcx> },
}
+1 -11
View File
@@ -5,7 +5,7 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_data_structures::{outline, sharded, sync};
use rustc_errors::{FatalError, StashKey};
use rustc_errors::FatalError;
use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex};
use rustc_middle::query::plumbing::QueryVTable;
use rustc_middle::query::{
@@ -110,16 +110,6 @@ fn mk_cycle<'tcx, C: QueryCache>(
let guar = error.delay_as_bug();
(query.value_from_cycle_error)(tcx, cycle_error, guar)
}
CycleErrorHandling::Stash => {
let guar = if let Some(root) = cycle_error.cycle.first()
&& let Some(span) = root.frame.info.span
{
error.stash(span, StashKey::Cycle).unwrap()
} else {
error.emit()
};
(query.value_from_cycle_error)(tcx, cycle_error, guar)
}
}
}
@@ -12,7 +12,6 @@
use rustc_middle::queries::QueryVTables;
use rustc_middle::query::CycleError;
use rustc_middle::query::erase::erase_val;
use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
@@ -31,9 +30,6 @@ pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) {
vtables.erase_and_anonymize_regions_ty.value_from_cycle_error =
|tcx, _, guar| erase_val(Ty::new_error(tcx, guar));
vtables.type_of_opaque.value_from_cycle_error =
|_, _, guar| erase_val(Err(CyclePlaceholder(guar)));
vtables.fn_sig.value_from_cycle_error = |tcx, cycle, guar| erase_val(fn_sig(tcx, cycle, guar));
vtables.check_representability.value_from_cycle_error =
@@ -756,11 +756,6 @@ pub fn report_selection_error(
}
}
SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => return self.report_opaque_type_auto_trait_leakage(
&obligation,
def_id,
),
SelectionError::TraitDynIncompatible(did) => {
let violations = self.tcx.dyn_compatibility_violations(did);
report_dyn_incompatibility(self.tcx, span, None, did, violations)
@@ -3331,34 +3326,6 @@ fn report_cyclic_signature_error(
)
}
fn report_opaque_type_auto_trait_leakage(
&self,
obligation: &PredicateObligation<'tcx>,
def_id: DefId,
) -> ErrorGuaranteed {
let name = match self.tcx.local_opaque_ty_origin(def_id.expect_local()) {
hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } => {
"opaque type".to_string()
}
hir::OpaqueTyOrigin::TyAlias { .. } => {
format!("`{}`", self.tcx.def_path_debug_str(def_id))
}
};
let mut err = self.dcx().struct_span_err(
obligation.cause.span,
format!("cannot check whether the hidden type of {name} satisfies auto traits"),
);
err.note(
"fetching the hidden types of an opaque inside of the defining scope is not supported. \
You can try moving the opaque type and the item that actually registers a hidden type into a new submodule",
);
err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
self.note_obligation_cause(&mut err, &obligation);
self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err)
}
fn report_signature_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -2404,15 +2404,11 @@ fn constituent_types_for_auto_trait(
// We can resolve the opaque type to its hidden type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
match self.tcx().type_of_opaque(def_id) {
Ok(ty) => ty::Binder::dummy(AutoImplConstituents {
types: vec![ty.instantiate(self.tcx(), args)],
assumptions: vec![],
}),
Err(_) => {
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
}
}
let ty = self.tcx().type_of_opaque(def_id);
ty::Binder::dummy(AutoImplConstituents {
types: vec![ty.instantiate(self.tcx(), args)],
assumptions: vec![],
})
}
}
})
@@ -8,7 +8,7 @@ fn main() {}
// Cycles should work as the deferred obligations are
// independently resolved and only require the concrete
// return type, which can't depend on the obligation.
fn cycle1() -> impl Clone {
fn cycle1() -> impl Clone { //~ ERROR: cycle detected
send(cycle2().clone());
Rc::new(Cell::new(5))
@@ -16,7 +16,6 @@ fn cycle1() -> impl Clone {
fn cycle2() -> impl Clone {
send(cycle1().clone());
//~^ ERROR: cannot check whether the hidden type of opaque type satisfies auto traits
Rc::new(String::from("foo"))
}
@@ -1,22 +1,84 @@
error: cannot check whether the hidden type of opaque type satisfies auto traits
--> $DIR/auto-trait-leak.rs:18:10
|
LL | send(cycle1().clone());
| ---- ^^^^^^^^^^^^^^^^
| |
| required by a bound introduced by this call
|
= note: fetching the hidden types of an opaque inside of the defining scope is not supported. You can try moving the opaque type and the item that actually registers a hidden type into a new submodule
note: opaque type is declared here
error[E0391]: cycle detected when computing type of opaque `cycle1::{opaque#0}`
--> $DIR/auto-trait-leak.rs:11:16
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^
note: required by a bound in `send`
--> $DIR/auto-trait-leak.rs:4:12
|
LL | fn send<T: Send>(_: T) {}
| ^^^^ required by this bound in `send`
note: ...which requires borrow-checking `cycle1`...
--> $DIR/auto-trait-leak.rs:11:1
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `cycle1`...
--> $DIR/auto-trait-leak.rs:11:1
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires checking if `cycle1` contains FFI-unwind calls...
--> $DIR/auto-trait-leak.rs:11:1
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires building MIR for `cycle1`...
--> $DIR/auto-trait-leak.rs:11:1
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires match-checking `cycle1`...
--> $DIR/auto-trait-leak.rs:11:1
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `cycle1`...
--> $DIR/auto-trait-leak.rs:12:5
|
LL | send(cycle2().clone());
| ^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires evaluating trait selection obligation `cycle2::{opaque#0}: core::marker::Send`...
note: ...which requires computing type of opaque `cycle2::{opaque#0}`...
--> $DIR/auto-trait-leak.rs:17:16
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^
note: ...which requires borrow-checking `cycle2`...
--> $DIR/auto-trait-leak.rs:17:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `cycle2`...
--> $DIR/auto-trait-leak.rs:17:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires checking if `cycle2` contains FFI-unwind calls...
--> $DIR/auto-trait-leak.rs:17:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires building MIR for `cycle2`...
--> $DIR/auto-trait-leak.rs:17:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires match-checking `cycle2`...
--> $DIR/auto-trait-leak.rs:17:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `cycle2`...
--> $DIR/auto-trait-leak.rs:18:5
|
LL | send(cycle1().clone());
| ^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires evaluating trait selection obligation `cycle1::{opaque#0}: core::marker::Send`...
= note: ...which again requires computing type of opaque `cycle1::{opaque#0}`, completing the cycle
note: cycle used when computing type of `cycle1::{opaque#0}`
--> $DIR/auto-trait-leak.rs:11:16
|
LL | fn cycle1() -> impl Clone {
| ^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0391`.
@@ -11,7 +11,6 @@ fn foo() -> Bar
Bar: Send,
{
[0; 1 + 2]
//~^ ERROR: type annotations needed: cannot satisfy `Bar: Send`
}
fn main() {}
@@ -1,19 +1,3 @@
error[E0283]: type annotations needed: cannot satisfy `Bar: Send`
--> $DIR/in-where-clause.rs:13:9
|
LL | [0; 1 + 2]
| ^^^^^
|
= note: cannot satisfy `Bar: Send`
note: required by a bound in `foo`
--> $DIR/in-where-clause.rs:11:10
|
LL | fn foo() -> Bar
| --- required by a bound in this function
LL | where
LL | Bar: Send,
| ^^^^ required by this bound in `foo`
error[E0391]: cycle detected when computing type of opaque `Bar::{opaque#0}`
--> $DIR/in-where-clause.rs:5:12
|
@@ -77,7 +61,6 @@ LL | type Bar = impl Sized;
= note: cycle used when evaluating trait selection obligation `Bar: core::marker::Send`
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 2 previous errors
error: aborting due to 1 previous error
Some errors have detailed explanations: E0283, E0391.
For more information about an error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0391`.