Skip to content

Commit

Permalink
Rollup merge of rust-lang#113320 - oli-obk:eval_obligation_query, r=p…
Browse files Browse the repository at this point in the history
…etrochenkov,BoxyUwU

Add some extra information to opaque type cycle errors

Plus a bunch of cleanups.

This should help users debug query cycles due to auto trait checking. We'll probably want to fix cycle errors in most (or all?) cases by looking at the current item's hidden types (new solver does this), and by delaying the auto trait checks to after typeck.
  • Loading branch information
Dylan-DPC authored Jul 5, 2023
2 parents b73344f + 9e98feb commit 3b4b215
Show file tree
Hide file tree
Showing 18 changed files with 443 additions and 55 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,10 @@ pub enum SelectionError<'tcx> {
/// Signaling that an error has already been emitted, to avoid
/// multiple errors being shown.
ErrorReporting,
/// 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),
}

#[derive(Clone, Debug, TypeVisitable, Lift)]
Expand Down
16 changes: 5 additions & 11 deletions compiler/rustc_query_system/src/query/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,33 +126,27 @@ where

#[cold]
#[inline(never)]
fn mk_cycle<Q, Qcx>(
query: Q,
qcx: Qcx,
cycle_error: CycleError<Qcx::DepKind>,
handler: HandleCycleError,
) -> Q::Value
fn mk_cycle<Q, Qcx>(query: Q, qcx: Qcx, cycle_error: CycleError<Qcx::DepKind>) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
handle_cycle_error(query, qcx, &cycle_error, error, handler)
handle_cycle_error(query, qcx, &cycle_error, error)
}

fn handle_cycle_error<Q, Qcx>(
query: Q,
qcx: Qcx,
cycle_error: &CycleError<Qcx::DepKind>,
mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
handler: HandleCycleError,
) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
{
use HandleCycleError::*;
match handler {
match query.handle_cycle_error() {
Error => {
error.emit();
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle)
Expand Down Expand Up @@ -277,7 +271,7 @@ where
&qcx.current_query_job(),
span,
);
(mk_cycle(query, qcx, error, query.handle_cycle_error()), None)
(mk_cycle(query, qcx, error), None)
}

#[inline(always)]
Expand Down Expand Up @@ -314,7 +308,7 @@ where

(v, Some(index))
}
Err(cycle) => (mk_cycle(query, qcx, cycle, query.handle_cycle_error()), None),
Err(cycle) => (mk_cycle(query, qcx, cycle), None),
}
}

Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_query_system/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ pub trait Value<Tcx: DepContext, D: DepKind>: Sized {
}

impl<Tcx: DepContext, T, D: DepKind> Value<Tcx, D> for T {
default fn from_cycle_error(tcx: Tcx, _: &[QueryInfo<D>]) -> T {
default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>]) -> T {
tcx.sess().abort_if_errors();
// Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
// non-trivial to define it earlier.
panic!("Value::from_cycle_error called without errors");
panic!(
"<{} as Value>::from_cycle_error called without errors: {cycle:#?}",
std::any::type_name::<T>()
);
}
}
58 changes: 48 additions & 10 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_middle::traits::select::OverflowError;
use rustc_middle::traits::solve::Goal;
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch};
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
Expand Down Expand Up @@ -1152,6 +1152,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}

SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
&obligation,
def_id,
),

TraitNotObjectSafe(did) => {
let violations = self.tcx.object_safety_violations(did);
report_object_safety_error(self.tcx, span, did, violations)
Expand All @@ -1170,16 +1175,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}

// Already reported in the query.
SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => {
// FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token.
self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error");
return;
}
SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) |
// Already reported.
Overflow(OverflowError::Error(_)) => {
self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported");
return;
}
Overflow(OverflowError::Error(_)) => return,

Overflow(_) => {
bug!("overflow should be handled before the `report_selection_error` path");
}
Expand Down Expand Up @@ -1471,6 +1470,12 @@ trait InferCtxtPrivExt<'tcx> {
terr: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;

fn report_opaque_type_auto_trait_leakage(
&self,
obligation: &PredicateObligation<'tcx>,
def_id: DefId,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;

fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
Expand Down Expand Up @@ -3192,6 +3197,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
}

fn report_opaque_type_auto_trait_leakage(
&self,
obligation: &PredicateObligation<'tcx>,
def_id: DefId,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
format!("opaque type")
}
hir::OpaqueTyOrigin::TyAlias { .. } => {
format!("`{}`", self.tcx.def_path_debug_str(def_id))
}
};
let mut err = self.tcx.sess.struct_span_err(
obligation.cause.span,
format!("cannot check whether the hidden type of {name} satisfies auto traits"),
);
err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
match self.defining_use_anchor {
DefiningAnchor::Bubble | DefiningAnchor::Error => {}
DefiningAnchor::Bind(bind) => {
err.span_note(
self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)),
"this item depends on auto traits of the hidden type, \
but may also be registering the hidden type. \
This is not supported right now. \
You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(),
);
}
};
err
}

fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

AutoImplCandidate => {
let data = self.confirm_auto_impl_candidate(obligation);
let data = self.confirm_auto_impl_candidate(obligation)?;
ImplSource::Builtin(data)
}

Expand Down Expand Up @@ -376,12 +376,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_auto_impl_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Vec<PredicateObligation<'tcx>> {
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_auto_impl_candidate");

let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty());
let types = self.constituent_types_for_ty(self_ty);
self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)
let types = self.constituent_types_for_ty(self_ty)?;
Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types))
}

/// See `confirm_auto_impl_candidate`.
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2294,8 +2294,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn constituent_types_for_ty(
&self,
t: ty::Binder<'tcx, Ty<'tcx>>,
) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
match *t.skip_binder().kind() {
) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
Ok(match *t.skip_binder().kind() {
ty::Uint(_)
| ty::Int(_)
| ty::Bool
Expand Down Expand Up @@ -2359,12 +2359,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}

ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
let ty = self.tcx().type_of(def_id);
if ty.skip_binder().references_error() {
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
}
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)])
t.rebind(vec![ty.subst(self.tcx(), substs)])
}
}
})
}

fn collect_predicates_for_types(
Expand Down
1 change: 1 addition & 0 deletions tests/ui/generator/layout-error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ fn main() {
type F = impl Future;
// Check that statics are inhabited computes they layout.
static POOL: Task<F> = Task::new();
//~^ ERROR: cannot check whether the hidden type of `layout_error[b009]::main::F::{opaque#0}` satisfies auto traits
Task::spawn(&POOL, || cb());
}
20 changes: 19 additions & 1 deletion tests/ui/generator/layout-error.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ error[E0425]: cannot find value `Foo` in this scope
LL | let a = Foo;
| ^^^ not found in this scope

error: aborting due to previous error
error: cannot check whether the hidden type of `layout_error[b009]::main::F::{opaque#0}` satisfies auto traits
--> $DIR/layout-error.rs:26:18
|
LL | static POOL: Task<F> = Task::new();
| ^^^^^^^
|
note: opaque type is declared here
--> $DIR/layout-error.rs:24:14
|
LL | type F = impl Future;
| ^^^^^^^^^^^
note: required because it appears within the type `Task<F>`
--> $DIR/layout-error.rs:9:12
|
LL | pub struct Task<F: Future>(F);
| ^^^^
= note: shared static variables must have a type that implements `Sync`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0425`.
6 changes: 4 additions & 2 deletions tests/ui/impl-trait/auto-trait-leak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ use std::rc::Rc;

fn send<T: Send>(_: T) {}

fn main() {
}
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 {
//~^ ERROR cycle detected
//~| ERROR cycle detected
send(cycle2().clone());
//~^ ERROR: cannot check whether the hidden type of opaque type satisfies auto traits

Rc::new(Cell::new(5))
}

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"))
}
Loading

0 comments on commit 3b4b215

Please sign in to comment.