diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 570d33aaf20e0..74887dd540ce3 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -23,6 +23,7 @@ use rustc_trait_selection::traits; use tracing::{debug, instrument, trace}; use super::{CoroutineTypes, Expectation, FnCtxt, check_fn}; +use crate::fn_ctxt::UseSubtyping; /// What signature do we *expect* the closure to have from context? #[derive(Debug, Clone, TypeFoldable, TypeVisitable)] @@ -317,7 +318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( Ty::new_var(self.tcx, self.root_var(vid)), closure_kind, - self.obligations_for_self_ty(vid) + self.obligations_for_self_ty(vid, UseSubtyping::No) .into_iter() .map(|obl| (obl.predicate, obl.cause.span)), ), @@ -587,7 +588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. let mut return_ty = None; - for bound in self.obligations_for_self_ty(return_vid) { + for bound in self.obligations_for_self_ty(return_vid, UseSubtyping::No) { if let Some(ret_projection) = bound.predicate.as_projection_clause() && let Some(ret_projection) = ret_projection.no_bound_vars() && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput) @@ -986,9 +987,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).into_iter().find_map(|obligation| { - get_future_output(obligation.predicate, obligation.cause.span) - })? + self.obligations_for_self_ty(ret_vid, UseSubtyping::No).into_iter().find_map( + |obligation| get_future_output(obligation.predicate, obligation.cause.span), + )? } ty::Alias(ty::AliasTy { kind: ty::Projection { .. }, .. }) => { return Some(Ty::new_error_with_message( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c8c6de4f99c03..af28bc241be5d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -720,14 +720,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { let sized_did = self.tcx.lang_items().sized_trait(); - self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { - match obligation.predicate.kind().skip_binder() { + + // NB: `T: Sized` implies that all subtypes and all supertypes of `T` are also sized, + // so it's valid to use subtyping here. (subtyping has to preserve layout and + // `T <: U => &T <: &U`, so subtyping can't change sizedness) + self.obligations_for_self_ty(self_ty, super::UseSubtyping::Yes).into_iter().any( + |obligation| match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { Some(data.def_id()) == sized_did } _ => false, - } - }) + }, + ) } pub(crate) fn err_args(&self, len: usize, guar: ErrorGuaranteed) -> Vec> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 8142b6ad57f3f..ce1d650af6b9d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -13,19 +13,38 @@ use tracing::{debug, instrument, trace}; use crate::FnCtxt; +/// Whatever to use subtyping or not when inspecting obligations +#[derive(Debug, Copy, Clone)] +pub(crate) enum UseSubtyping { + /// Do **not** use subtyping. [`FnCtxt::obligations_for_self_ty`] will only return obligations + /// where the self type is known to be equal to the provided vid. + No, + + /// Use subtyping. [`FnCtxt::obligations_for_self_ty`] will return obligations + /// where the self type is related to the provided vid via subtyping. + /// + /// Using this requires extra care, as traits holding for a subtype or a supertype, does not + /// necessarily imply that they hold for the respective supertype or subtype. + Yes, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a list of all obligations whose self type has been unified /// with the unconstrained type `self_ty`. #[instrument(skip(self), level = "debug")] - pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> { + pub(crate) fn obligations_for_self_ty( + &self, + self_ty: ty::TyVid, + subtyping: UseSubtyping, + ) -> PredicateObligations<'tcx> { if self.next_trait_solver() { - self.obligations_for_self_ty_next(self_ty) + self.obligations_for_self_ty_next(self_ty, subtyping) } else { - let ty_var_root = self.root_var(self_ty); let mut obligations = self.fulfillment_cx.borrow().pending_obligations(); trace!("pending_obligations = {:#?}", obligations); - obligations - .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root)); + obligations.retain(|obligation| { + self.predicate_has_self_ty(obligation.predicate, self_ty, subtyping) + }); obligations } } @@ -35,14 +54,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, predicate: ty::Predicate<'tcx>, expected_vid: ty::TyVid, + subtyping: UseSubtyping, ) -> bool { match predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - self.type_matches_expected_vid(data.self_ty(), expected_vid) + self.type_matches_expected_vid(data.self_ty(), expected_vid, subtyping) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { if data.projection_term.kind.is_trait_projection() { - self.type_matches_expected_vid(data.self_ty(), expected_vid) + self.type_matches_expected_vid(data.self_ty(), expected_vid, subtyping) } else { false } @@ -65,14 +85,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - fn type_matches_expected_vid(&self, ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { + fn type_matches_expected_vid( + &self, + ty: Ty<'tcx>, + expected_vid: ty::TyVid, + subtyping: UseSubtyping, + ) -> bool { let ty = self.shallow_resolve(ty); debug!(?ty); match *ty.kind() { - ty::Infer(ty::TyVar(found_vid)) => { - self.root_var(expected_vid) == self.root_var(found_vid) - } + ty::Infer(ty::TyVar(found_vid)) => match subtyping { + UseSubtyping::No => self.root_var(expected_vid) == self.root_var(found_vid), + UseSubtyping::Yes => { + self.sub_unification_table_root_var(expected_vid) + == self.sub_unification_table_root_var(found_vid) + } + }, _ => false, } } @@ -80,6 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn obligations_for_self_ty_next( &self, self_ty: ty::TyVid, + subtyping: UseSubtyping, ) -> PredicateObligations<'tcx> { // We only look at obligations which may reference the self type. // This lookup uses the `sub_root` instead of the inference variable @@ -94,6 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .pending_obligations_potentially_referencing_sub_root(&self.infcx, sub_root_var); debug!(?obligations); + let mut obligations_for_self_ty = PredicateObligations::new(); for obligation in obligations { let mut visitor = NestedObligationsForSelfTy { @@ -101,6 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, obligations_for_self_ty: &mut obligations_for_self_ty, root_cause: &obligation.cause, + subtyping, }; let goal = obligation.as_goal(); @@ -183,6 +215,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> { self_ty: ty::TyVid, root_cause: &'a ObligationCause<'tcx>, obligations_for_self_ty: &'a mut PredicateObligations<'tcx>, + subtyping: UseSubtyping, } impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { @@ -210,7 +243,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { .orig_values() .iter() .filter_map(|arg| arg.as_type()) - .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty)) + .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty, self.subtyping)) { debug!(goal = ?inspect_goal.goal(), "goal does not mention self type"); return; @@ -218,7 +251,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty, self.subtyping) { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 922a9d7e95bda..8530f42bc0f66 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -8,6 +8,7 @@ mod suggestions; use std::cell::{Cell, RefCell}; use std::ops::Deref; +pub(crate) use inspect_obligations::UseSubtyping; use rustc_errors::DiagCtxtHandle; use rustc_hir::attrs::{DivergingBlockBehavior, DivergingFallbackBehavior}; use rustc_hir::def_id::{DefId, LocalDefId}; diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 4c56bf0923c39..21ba07428d9ec 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -246,13 +246,11 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { } /// Returns the "root" variable of `vid` in the `sub_unification_table` - /// equivalence table. All type variables that have been are related via + /// equivalence table. All type variables that have been related via /// equality or subtyping will yield the same root variable (per the /// union-find algorithm), so `sub_unification_table_root_var(a) - /// == sub_unification_table_root_var(b)` implies that: - /// ```text - /// exists X. (a <: X || X <: a) && (b <: X || X <: b) - /// ``` + /// == sub_unification_table_root_var(b)` implies that `a` and `b` are + /// transitively related via subtyping. pub(crate) fn sub_unification_table_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.sub_unification_table().find(vid).vid } diff --git a/tests/ui/impl-trait/unsized_coercion.next.stderr b/tests/ui/impl-trait/unsized_coercion.next.stderr deleted file mode 100644 index f5ca850336fbc..0000000000000 --- a/tests/ui/impl-trait/unsized_coercion.next.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:12:15 - | -LL | fn hello() -> Box { - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:15:17 - | -LL | let x = hello(); - | ^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion.rs b/tests/ui/impl-trait/unsized_coercion.rs index 3f5ec3de89f31..f77f2198be0ef 100644 --- a/tests/ui/impl-trait/unsized_coercion.rs +++ b/tests/ui/impl-trait/unsized_coercion.rs @@ -3,17 +3,15 @@ //@ revisions: next old //@[next] compile-flags: -Znext-solver -//@[old] check-pass +//@ check-pass trait Trait {} impl Trait for u32 {} fn hello() -> Box { -//[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time if true { let x = hello(); - //[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time let y: Box = x; } Box::new(1u32) diff --git a/tests/ui/sized/infer_var_subtyping.rs b/tests/ui/sized/infer_var_subtyping.rs new file mode 100644 index 0000000000000..2535a71aac619 --- /dev/null +++ b/tests/ui/sized/infer_var_subtyping.rs @@ -0,0 +1,19 @@ +// Regression test for failure #4 in . +// +// This checks that when we are checking the sized-ness of an inference variable +// (here coming from the never-to-any coercion) we consider subtyping. That is, +// `?0` is sized if `(?0 <: ?1 || ?0 :> ?1) && ?1: Sized`). +// +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(never_type)] + +fn blah(e: !) { + let source = Box::new(e); + let _: Box = source; +} + +fn main() {}