From 579a337306e0f949dab60e4bbd68418a834d5373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 15 Oct 2023 13:40:17 +0200 Subject: [PATCH 1/2] Normalize trait ref before orphan check & consider ty params in alias types to be uncovered --- compiler/rustc_hir_analysis/messages.ftl | 8 +- .../src/coherence/orphan.rs | 78 ++++++++---- compiler/rustc_hir_analysis/src/errors.rs | 6 +- compiler/rustc_trait_selection/src/lib.rs | 1 + .../src/traits/coherence.rs | 118 +++++++++++++----- .../coherence/auxiliary/parametrized-trait.rs | 2 + .../auxiliary/trait-with-assoc-ty.rs | 3 + .../orphan-check-projections-covering.rs | 25 ++++ .../orphan-check-projections-nested.rs | 23 ++++ ...ions-not-covering-ambiguity.classic.stderr | 12 ++ ...ections-not-covering-ambiguity.next.stderr | 12 ++ ...heck-projections-not-covering-ambiguity.rs | 27 ++++ ...ck-projections-not-covering.classic.stderr | 30 +++++ ...check-projections-not-covering.next.stderr | 30 +++++ .../orphan-check-projections-not-covering.rs | 37 ++++++ ...ck-projections-unsat-bounds.classic.stderr | 13 ++ ...check-projections-unsat-bounds.next.stderr | 13 ++ .../orphan-check-projections-unsat-bounds.rs | 43 +++++++ .../orphan-check-weak-aliases-covering.rs | 20 +++ ...k-weak-aliases-not-covering.classic.stderr | 12 ++ ...heck-weak-aliases-not-covering.next.stderr | 12 ++ .../orphan-check-weak-aliases-not-covering.rs | 19 +++ .../ui/type-alias-impl-trait/coherence.stderr | 2 +- 23 files changed, 483 insertions(+), 63 deletions(-) create mode 100644 tests/ui/coherence/auxiliary/parametrized-trait.rs create mode 100644 tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs create mode 100644 tests/ui/coherence/orphan-check-projections-covering.rs create mode 100644 tests/ui/coherence/orphan-check-projections-nested.rs create mode 100644 tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs create mode 100644 tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-not-covering.next.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-not-covering.rs create mode 100644 tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-unsat-bounds.rs create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-covering.rs create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 89cd37e757fa2..5e95296d52f0b 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -429,13 +429,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de .label = needs at most one field with non-trivial size or alignment, but has {$field_count} .labels = this field has non-zero size or requires alignment -hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) - .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) +hir_analysis_ty_param_first_local = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) + .label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type .case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last -hir_analysis_ty_param_some = type parameter `{$param_ty}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param_ty}>`) - .label = type parameter `{$param_ty}` must be used as the type parameter for some local type +hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`) + .label = type parameter `{$param}` must be used as the type parameter for some local type .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local .only_note = only traits defined in the current crate can be implemented for a type parameter diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index b46a67d08eb03..c5524817111e1 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -2,11 +2,15 @@ //! crate or pertains to a type defined in this crate. use crate::errors; + +use std::ops::ControlFlow; + +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_span::def_id::LocalDefId; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::traits::{self, IsFirstInputType}; #[instrument(skip(tcx), level = "debug")] @@ -23,20 +27,17 @@ pub(crate) fn orphan_check_impl( Ok(()) => {} Err(err) => { let item = tcx.hir().expect_item(impl_def_id); - let hir::ItemKind::Impl(impl_) = item.kind else { - bug!("{:?} is not an impl: {:?}", impl_def_id, item); - }; - let tr = impl_.of_trait.as_ref().unwrap(); - let sp = tcx.def_span(impl_def_id); + let impl_ = item.expect_impl(); + let hir_trait_ref = impl_.of_trait.as_ref().unwrap(); emit_orphan_check_error( tcx, - sp, + tcx.def_span(impl_def_id), item.span, - tr.path.span, + hir_trait_ref.path.span, trait_ref, impl_.self_ty.span, - impl_.generics, + tcx.generics_of(impl_def_id), err, )? } @@ -277,7 +278,7 @@ fn emit_orphan_check_error<'tcx>( trait_span: Span, trait_ref: ty::TraitRef<'tcx>, self_ty_span: Span, - generics: &hir::Generics<'tcx>, + generics: &'tcx ty::Generics, err: traits::OrphanCheckErr<'tcx>, ) -> Result { let self_ty = trait_ref.self_ty(); @@ -404,23 +405,48 @@ fn emit_orphan_check_error<'tcx>( }; tcx.dcx().emit_err(err_struct) } - traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { - let mut sp = sp; - for param in generics.params { - if param.name.ident().to_string() == param_ty.to_string() { - sp = param.span; - } + // FIXME(fmease): The uncovered ty may contain infer vars. + // Somwhere we need to remap them to ty params. + traits::OrphanCheckErr::UncoveredTy(uncovered_ty, local_type) => { + let mut collector = + UncoveredTyParamCollector { tcx, generics, uncovered_params: Default::default() }; + uncovered_ty.visit_with(&mut collector); + + let mut reported = None; + for (param, span) in collector.uncovered_params { + reported.get_or_insert(match local_type { + Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { + span, + note: (), + param, + local_type, + }), + None => tcx.dcx().emit_err(errors::TyParamSome { span, note: (), param }), + }); } - match local_type { - Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { - span: sp, - note: (), - param_ty, - local_type, - }), - None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }), + struct UncoveredTyParamCollector<'tcx> { + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + uncovered_params: FxIndexMap, } + + impl<'tcx> TypeVisitor> for UncoveredTyParamCollector<'tcx> { + type BreakTy = !; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::Param(param_ty) = *ty.kind() { + let param_def = self.generics.param_at(param_ty.index as _, self.tcx); + let span = self.tcx.def_ident_span(param_def.def_id).unwrap(); + self.uncovered_params.insert(param_def.name, span); + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) + } + } + } + + reported.unwrap_or_else(|| bug!("failed to find ty param in `{uncovered_ty}`")) } }) } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d217d16ed8421..b22c576271417 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1328,20 +1328,20 @@ pub struct TyParamFirstLocal<'a> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param_ty: Ty<'a>, + pub param: Symbol, pub local_type: Ty<'a>, } #[derive(Diagnostic)] #[diag(hir_analysis_ty_param_some, code = E0210)] #[note] -pub struct TyParamSome<'a> { +pub struct TyParamSome { #[primary_span] #[label] pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param_ty: Ty<'a>, + pub param: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 00a2adccf64a5..d3a184df58196 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -20,6 +20,7 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(option_take_if)] #![feature(never_type)] diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index e82171de3789e..2e8af2f352354 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -12,15 +12,15 @@ use crate::solve::{deeply_normalize_for_diagnostics, inspect, FulfillmentCtxt}; use crate::traits::engine::TraitEngineExt as _; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::structural_normalize::StructurallyNormalizeExt; -use crate::traits::NormalizeExt; use crate::traits::SkipLeakCheck; +use crate::traits::{NormalizeExt, ObligationCtxt}; use crate::traits::{ Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine, TraitEngineExt}; use rustc_middle::traits::query::NoSolution; @@ -598,7 +598,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, ) -> bool { - trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental) + trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental) } #[derive(Debug, Copy, Clone)] @@ -630,7 +630,7 @@ pub enum OrphanCheckErr<'tcx> { /// 2. Some local type must appear in `Self`. #[instrument(level = "debug", skip(tcx), ret)] pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> { - // We only except this routine to be invoked on implementations + // We only accept this routine to be invoked on implementations // of a trait, not inherent implementations. let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); debug!(?trait_ref); @@ -641,7 +641,35 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe return Ok(()); } - orphan_check_trait_ref::(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap() + orphan_check_trait_ref::(trait_ref, InCrate::Local, |raw_ty| { + let ty::Alias(..) = raw_ty.kind() else { return Ok(raw_ty) }; + + let infcx = tcx.infer_ctxt().intercrate(true).build(); + let cause = ObligationCause::dummy(); + + let args = infcx.fresh_args_for_item(cause.span, impl_def_id); + let raw_ty = ty::EarlyBinder::bind(raw_ty).instantiate(tcx, args); + + let ocx = ObligationCtxt::new(&infcx); + let ty = ocx.normalize(&cause, ty::ParamEnv::empty(), raw_ty); + let ty = infcx.resolve_vars_if_possible(ty); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + return Ok(raw_ty); + } + + if infcx.next_trait_solver() { + let mut fulfill_cx = >::new(&infcx); + infcx + .at(&cause, ty::ParamEnv::empty()) + .structurally_normalize(ty, &mut *fulfill_cx) + .map(|ty| infcx.resolve_vars_if_possible(ty)) + .or(Ok(ty)) + } else { + Ok(ty) + } + }) + .unwrap() } /// Checks whether a trait-ref is potentially implementable by a crate. @@ -689,6 +717,9 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// To check that a local impl follows the orphan rules, we check it in /// InCrate::Local mode, using type parameters for the "generic" types. /// +/// In InCrate::Local mode the orphan check succeeds if the current crate +/// is definitely allowed to implement the given trait (no false positives). +/// /// 2. They ground negative reasoning for coherence. If a user wants to /// write both a conditional blanket impl and a specific impl, we need to /// make sure they do not overlap. For example, if we write @@ -707,6 +738,9 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// try to implement this trait-ref. To check for this, we use InCrate::Remote /// mode. That is sound because we already know all the impls from known crates. /// +/// In InCrate::Remote mode the orphan check succeeds if a foreign crate +/// *could* implement the given trait (no false negatives). +/// /// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can /// add "non-blanket" impls without breaking negative reasoning in dependent /// crates. This is the "rebalancing coherence" (RFC 1023) restriction. @@ -747,16 +781,14 @@ fn orphan_check_trait_ref<'tcx, E: Debug>( Ok(match trait_ref.visit_with(&mut checker) { ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err), - ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => { + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) => { // Does there exist some local type after the `ParamTy`. checker.search_first_local_ty = true; - if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) = - trait_ref.visit_with(&mut checker).break_value() - { - Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty))) - } else { - Err(OrphanCheckErr::UncoveredTy(ty, None)) - } + let local_ty = match trait_ref.visit_with(&mut checker).break_value() { + Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), + _ => None, + }; + Err(OrphanCheckErr::UncoveredTy(ty, local_ty)) } ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), }) @@ -791,11 +823,14 @@ where ControlFlow::Continue(()) } - fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow> { + fn found_uncovered_ty_param( + &mut self, + t: Ty<'tcx>, + ) -> ControlFlow> { if self.search_first_local_ty { ControlFlow::Continue(()) } else { - ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t)) + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(t)) } } @@ -809,7 +844,7 @@ where enum OrphanCheckEarlyExit<'tcx, E> { NormalizationFailure(E), - ParamTy(Ty<'tcx>), + UncoveredTyParam(Ty<'tcx>), LocalTy(Ty<'tcx>), } @@ -823,9 +858,9 @@ where } fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - // Need to lazily normalize here in with `-Znext-solver=coherence`. let ty = match (self.lazily_normalize_ty)(ty) { - Ok(ty) => ty, + Ok(norm_ty) if norm_ty.is_ty_or_numeric_infer() => ty, + Ok(norm_ty) => norm_ty, Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), }; @@ -842,19 +877,44 @@ where | ty::Slice(..) | ty::RawPtr(..) | ty::Never - | ty::Tuple(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { - self.found_non_local_ty(ty) - } - - ty::Param(..) => self.found_param_ty(ty), + | ty::Tuple(..) => self.found_non_local_ty(ty), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate { - InCrate::Local => self.found_non_local_ty(ty), - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Param(..) => match self.in_crate { + InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Remote => bug!("unexpected ty param"), }, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { + match self.in_crate { + InCrate::Local => self.found_uncovered_ty_param(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + } + } + + ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { + if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) { + match self.in_crate { + InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Remote => bug!("unexpected ty param"), + } + } else if ty.has_type_flags( + ty::TypeFlags::HAS_TY_PLACEHOLDER + | ty::TypeFlags::HAS_TY_BOUND + | ty::TypeFlags::HAS_TY_INFER, + ) { + match self.in_crate { + InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Remote => { + // The inference variable might be unified with a local + // type in that remote crate. + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } + } + } else { + ControlFlow::Continue(()) + } + } // For fundamental types, we just look inside of them. ty::Ref(_, ty, _) => ty.visit_with(self), diff --git a/tests/ui/coherence/auxiliary/parametrized-trait.rs b/tests/ui/coherence/auxiliary/parametrized-trait.rs new file mode 100644 index 0000000000000..88a3d5cd52dc0 --- /dev/null +++ b/tests/ui/coherence/auxiliary/parametrized-trait.rs @@ -0,0 +1,2 @@ +pub trait Trait0 {} +pub trait Trait1 {} diff --git a/tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs b/tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs new file mode 100644 index 0000000000000..d49538de580dd --- /dev/null +++ b/tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs @@ -0,0 +1,3 @@ +pub trait Trait { + type Assoc; +} diff --git a/tests/ui/coherence/orphan-check-projections-covering.rs b/tests/ui/coherence/orphan-check-projections-covering.rs new file mode 100644 index 0000000000000..ae1917ec161f1 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-covering.rs @@ -0,0 +1,25 @@ +// Projections cover type parameters if they normalize to a (local) type that covers them. +// This ensures that we don't perform an overly strict check on +// projections like in closed PR #100555 which did a syntactic +// check for type parameters in projections without normalizing +// first which would've lead to real-word regressions. + +//@ check-pass +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +trait Project { type Output; } + +impl Project for Wrapper { + type Output = Local; +} + +struct Wrapper(T); +struct Local; + +impl foreign::Trait1 for as Project>::Output {} + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-projections-nested.rs b/tests/ui/coherence/orphan-check-projections-nested.rs new file mode 100644 index 0000000000000..ec244a8005b16 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-nested.rs @@ -0,0 +1,23 @@ +// This used to ICE in an earlier iteration of #117164. Minimized from crate `proqnt`. + +//@ check-pass +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver +//@ aux-crate:dep=trait-with-assoc-ty.rs +//@ edition: 2021 + +pub(crate) trait Trait { + type Assoc; +} + +pub(crate) struct Type(T, U, V); + +impl dep::Trait for Type::Assoc as Trait>::Assoc, U> +where + T: dep::Trait, + ::Assoc: Trait, +{ + type Assoc = U; +} + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr new file mode 100644 index 0000000000000..7522f6b9084b3 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr @@ -0,0 +1,12 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering-ambiguity.rs:24:6 + | +LL | impl foreign::Trait1 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr new file mode 100644 index 0000000000000..7522f6b9084b3 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr @@ -0,0 +1,12 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering-ambiguity.rs:24:6 + | +LL | impl foreign::Trait1 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs new file mode 100644 index 0000000000000..374ad44c8029d --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs @@ -0,0 +1,27 @@ +// This test demonstrates a limitation of the trait solver. +// Basically, one might think that `T` was covered by the projection since the +// latter appears to normalize to a local type. However, since we instantiate the +// constituent types of the self type of impls with fresh infer vars and try to +// normalize them during orphan checking, we wind up trying to normalize a +// projection whose self type is an infer var which unconditionally fails due to +// ambiguity. + +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ compile-flags: --crate-type=lib +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +trait Project { type Output; } + +impl Project for T { + type Output = Local; +} + +struct Local; + +impl foreign::Trait1 for ::Output {} +//~^ ERROR type parameter `T` must be covered by another type + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr new file mode 100644 index 0000000000000..f61432e229c83 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr @@ -0,0 +1,30 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering.rs:21:6 + | +LL | impl foreign::Trait0 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering.rs:24:6 + | +LL | impl foreign::Trait0<::Output, Local, T> for Option {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering.rs:36:6 + | +LL | impl foreign::Trait1 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr new file mode 100644 index 0000000000000..f61432e229c83 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr @@ -0,0 +1,30 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering.rs:21:6 + | +LL | impl foreign::Trait0 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering.rs:24:6 + | +LL | impl foreign::Trait0<::Output, Local, T> for Option {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-not-covering.rs:36:6 + | +LL | impl foreign::Trait1 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.rs b/tests/ui/coherence/orphan-check-projections-not-covering.rs new file mode 100644 index 0000000000000..2b6eea73848c5 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-not-covering.rs @@ -0,0 +1,37 @@ +// Regression test for issue #99554. +// Projections might not cover type parameters. + +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ compile-flags: --crate-type=lib +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +trait Identity { + type Output; +} + +impl Identity for T { + type Output = T; +} + +struct Local; + +impl foreign::Trait0 for ::Output {} +//~^ ERROR type parameter `T` must be covered by another type + +impl foreign::Trait0<::Output, Local, T> for Option {} +//~^ ERROR type parameter `T` must be covered by another type + +pub trait Deferred { + type Output; +} + +// A downstream user could implement +// +// impl Deferred for Type { type Output = T; } +// struct Type(T); +// +impl foreign::Trait1 for ::Output {} +//~^ ERROR type parameter `T` must be covered by another type diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr new file mode 100644 index 0000000000000..3559b226ff738 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr @@ -0,0 +1,13 @@ +error: internal compiler error: compiler/rustc_hir_analysis/src/coherence/orphan.rs:449:40: failed to find ty param in ` as Discard>::Output` + +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + +query stack during panic: +#0 [orphan_check_impl] checking whether impl `` follows the orphan rules +#1 [coherent_trait] coherence checking all impls of trait `parametrized_trait::Trait1` +#2 [analysis] running analysis passes on this crate +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr new file mode 100644 index 0000000000000..3559b226ff738 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr @@ -0,0 +1,13 @@ +error: internal compiler error: compiler/rustc_hir_analysis/src/coherence/orphan.rs:449:40: failed to find ty param in ` as Discard>::Output` + +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + +query stack during panic: +#0 [orphan_check_impl] checking whether impl `` follows the orphan rules +#1 [coherent_trait] coherence checking all impls of trait `parametrized_trait::Trait1` +#2 [analysis] running analysis passes on this crate +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs b/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs new file mode 100644 index 0000000000000..8f987a98712fe --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs @@ -0,0 +1,43 @@ +// This used to ICE in an earlier iteration of #117164. +// The normalization performed during orphan checking happens inside an +// empty ParamEnv and with type parameters mapped to fresh infer vars. +// Therefore it may fail while normalization outside of orphan checking +// succeeds, e.g. due to unsatisfied bounds. + +//@ known-bug: unknown +//@ failure-status: 101 +//@ normalize-stderr-test: "DefId\(.*?\]::" -> "DefId(" +//@ normalize-stderr-test: "(?m)note: we would appreciate a bug report.*\n\n" -> "" +//@ normalize-stderr-test: "(?m)note: rustc.*running on.*\n\n" -> "" +//@ normalize-stderr-test: "(?m)note: compiler flags.*\n\n" -> "" +//@ normalize-stderr-test: "(?m)note: delayed at.*$" -> "" +//@ normalize-stderr-test: "(?m)^ *\d+: .*\n" -> "" +//@ normalize-stderr-test: "(?m)^ *at .*\n" -> "" + +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +struct Wrapper(T); + +trait Bound {} + +trait Discard { type Output; } + +impl Discard for Wrapper +where + Wrapper: Bound +{ + type Output = LocalTy; +} + +struct LocalTy; + +impl foreign::Trait1 for as Discard>::Output +where + Wrapper: Bound +{} + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-weak-aliases-covering.rs b/tests/ui/coherence/orphan-check-weak-aliases-covering.rs new file mode 100644 index 0000000000000..a8b9e905c70b5 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-covering.rs @@ -0,0 +1,20 @@ +// Weak aliases cover type parameters if they normalize to a (local) type that covers them. + +//@ check-pass +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Alias = LocalWrapper; + +struct Local; +struct LocalWrapper(T); + +impl foreign::Trait1 for Alias {} + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr new file mode 100644 index 0000000000000..276833fa17122 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr @@ -0,0 +1,12 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-weak-aliases-not-covering.rs:16:6 + | +LL | impl foreign::Trait1 for Identity {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr new file mode 100644 index 0000000000000..276833fa17122 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr @@ -0,0 +1,12 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-weak-aliases-not-covering.rs:16:6 + | +LL | impl foreign::Trait1 for Identity {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs new file mode 100644 index 0000000000000..9ebc45a882937 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs @@ -0,0 +1,19 @@ +// Weak aliases might not cover type parameters. + +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Identity = T; + +struct Local; + +impl foreign::Trait1 for Identity {} +//~^ ERROR type parameter `T` must be covered by another type + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/coherence.stderr b/tests/ui/type-alias-impl-trait/coherence.stderr index 4462b70f782ba..266a532a1db19 100644 --- a/tests/ui/type-alias-impl-trait/coherence.stderr +++ b/tests/ui/type-alias-impl-trait/coherence.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- | | | - | | `AliasOfForeignType<()>` is not defined in the current crate + | | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead From 55d96b36fdbcd58cbac3351183acb8461b4ba11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 26 Feb 2024 18:15:03 +0100 Subject: [PATCH 2/2] [hack] orphan checks: diagnostics: support infer vars in uncovered tys --- .../src/coherence/orphan.rs | 48 ++++++++++--- .../src/traits/coherence.rs | 70 ++++++++++++------- ...ck-projections-unsat-bounds.classic.stderr | 19 +++-- ...check-projections-unsat-bounds.next.stderr | 19 +++-- .../orphan-check-projections-unsat-bounds.rs | 11 +-- 5 files changed, 103 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index c5524817111e1..97f7ef1cb8aa3 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -7,6 +7,8 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; +use rustc_infer::infer::type_variable::TypeVariableOriginKind; +use rustc_infer::infer::InferCtxt; use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt}; use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_span::def_id::LocalDefId; @@ -407,9 +409,13 @@ fn emit_orphan_check_error<'tcx>( } // FIXME(fmease): The uncovered ty may contain infer vars. // Somwhere we need to remap them to ty params. - traits::OrphanCheckErr::UncoveredTy(uncovered_ty, local_type) => { - let mut collector = - UncoveredTyParamCollector { tcx, generics, uncovered_params: Default::default() }; + traits::OrphanCheckErr::UncoveredTy(uncovered_ty, infcx, local_type) => { + let mut collector = UncoveredTyParamCollector { + tcx, + infcx, + generics, + uncovered_params: Default::default(), + }; uncovered_ty.visit_with(&mut collector); let mut reported = None; @@ -427,22 +433,44 @@ fn emit_orphan_check_error<'tcx>( struct UncoveredTyParamCollector<'tcx> { tcx: TyCtxt<'tcx>, + infcx: Option>>, generics: &'tcx ty::Generics, uncovered_params: FxIndexMap, } + impl<'tcx> UncoveredTyParamCollector<'tcx> { + fn add_uncovered_param_ty(&mut self, index: u32) { + let param_def = self.generics.param_at(index as _, self.tcx); + let span = self.tcx.def_ident_span(param_def.def_id).unwrap(); + self.uncovered_params.insert(param_def.name, span); + } + } + impl<'tcx> TypeVisitor> for UncoveredTyParamCollector<'tcx> { type BreakTy = !; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - if let ty::Param(param_ty) = *ty.kind() { - let param_def = self.generics.param_at(param_ty.index as _, self.tcx); - let span = self.tcx.def_ident_span(param_def.def_id).unwrap(); - self.uncovered_params.insert(param_def.name, span); - ControlFlow::Continue(()) - } else { - ty.super_visit_with(self) + match *ty.kind() { + ty::Param(param_ty) => { + self.add_uncovered_param_ty(param_ty.index); + return ControlFlow::Continue(()); + } + ty::Infer(ty::TyVar(_)) => { + if let Some(infcx) = &self.infcx + && let Some(origin) = infcx.type_var_origin(ty) + && let TypeVariableOriginKind::TypeParameterDefinition(_, def_id) = + origin.kind + && let Some(index) = + self.generics.param_def_id_to_index(self.tcx, def_id) + { + self.add_uncovered_param_ty(index); + return ControlFlow::Continue(()); + } + } + _ => {} } + + ty.super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 2e8af2f352354..b79cc891b2f52 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -564,7 +564,11 @@ pub fn trait_ref_is_knowable<'tcx, E: Debug>( trait_ref: ty::TraitRef<'tcx>, mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result, E>, ) -> Result, E> { - if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() { + if orphan_check_trait_ref(trait_ref, InCrate::Remote, |ty| { + lazily_normalize_ty(ty).map(|ty| (ty, None)) + })? + .is_ok() + { // A downstream or cousin crate is allowed to implement some // generic parameters of this trait-ref. return Ok(Err(Conflict::Downstream)); @@ -587,7 +591,11 @@ pub fn trait_ref_is_knowable<'tcx, E: Debug>( // and if we are an intermediate owner, then we don't care // about future-compatibility, which means that we're OK if // we are an owner. - if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() { + if orphan_check_trait_ref(trait_ref, InCrate::Local, |ty| { + lazily_normalize_ty(ty).map(|ty| (ty, None)) + })? + .is_ok() + { Ok(Ok(())) } else { Ok(Err(Conflict::Upstream)) @@ -616,10 +624,20 @@ impl From for IsFirstInputType { } } -#[derive(Debug)] pub enum OrphanCheckErr<'tcx> { NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>), - UncoveredTy(Ty<'tcx>, Option>), + UncoveredTy(Ty<'tcx>, Option>>, Option>), +} + +impl<'tcx> Debug for OrphanCheckErr<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NonLocalInputType(ty) => f.debug_tuple("NonLocalInputType").field(ty).finish(), + Self::UncoveredTy(ty, _, local_ty) => { + f.debug_tuple("UncoveredTy").field(ty).field(local_ty).finish() + } + } + } } /// Checks the coherence orphan rules. `impl_def_id` should be the @@ -642,7 +660,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe } orphan_check_trait_ref::(trait_ref, InCrate::Local, |raw_ty| { - let ty::Alias(..) = raw_ty.kind() else { return Ok(raw_ty) }; + let ty::Alias(..) = raw_ty.kind() else { return Ok((raw_ty, None)) }; let infcx = tcx.infer_ctxt().intercrate(true).build(); let cause = ObligationCause::dummy(); @@ -655,19 +673,21 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe let ty = infcx.resolve_vars_if_possible(ty); let errors = ocx.select_where_possible(); if !errors.is_empty() { - return Ok(raw_ty); + return Ok((raw_ty, Some(Box::new(infcx)))); } - if infcx.next_trait_solver() { + let ty = if infcx.next_trait_solver() { let mut fulfill_cx = >::new(&infcx); infcx .at(&cause, ty::ParamEnv::empty()) .structurally_normalize(ty, &mut *fulfill_cx) .map(|ty| infcx.resolve_vars_if_possible(ty)) - .or(Ok(ty)) + .unwrap_or(ty) } else { - Ok(ty) - } + ty + }; + + Ok((ty, Some(Box::new(infcx)))) }) .unwrap() } @@ -768,7 +788,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe fn orphan_check_trait_ref<'tcx, E: Debug>( trait_ref: ty::TraitRef<'tcx>, in_crate: InCrate, - lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result, E>, + lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<(Ty<'tcx>, Option>>), E>, ) -> Result>, E> { if trait_ref.has_infer() && trait_ref.has_param() { bug!( @@ -781,14 +801,14 @@ fn orphan_check_trait_ref<'tcx, E: Debug>( Ok(match trait_ref.visit_with(&mut checker) { ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err), - ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) => { + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty, infcx)) => { // Does there exist some local type after the `ParamTy`. checker.search_first_local_ty = true; let local_ty = match trait_ref.visit_with(&mut checker).break_value() { Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), _ => None, }; - Err(OrphanCheckErr::UncoveredTy(ty, local_ty)) + Err(OrphanCheckErr::UncoveredTy(ty, infcx, local_ty)) } ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), }) @@ -806,7 +826,7 @@ struct OrphanChecker<'tcx, F> { impl<'tcx, F, E> OrphanChecker<'tcx, F> where - F: FnOnce(Ty<'tcx>) -> Result, E>, + F: FnOnce(Ty<'tcx>) -> Result<(Ty<'tcx>, Option>>), E>, { fn new(in_crate: InCrate, lazily_normalize_ty: F) -> Self { OrphanChecker { @@ -826,11 +846,12 @@ where fn found_uncovered_ty_param( &mut self, t: Ty<'tcx>, + infcx: Option>>, ) -> ControlFlow> { if self.search_first_local_ty { ControlFlow::Continue(()) } else { - ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(t)) + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(t, infcx)) } } @@ -844,13 +865,13 @@ where enum OrphanCheckEarlyExit<'tcx, E> { NormalizationFailure(E), - UncoveredTyParam(Ty<'tcx>), + UncoveredTyParam(Ty<'tcx>, Option>>), LocalTy(Ty<'tcx>), } impl<'tcx, F, E> TypeVisitor> for OrphanChecker<'tcx, F> where - F: FnMut(Ty<'tcx>) -> Result, E>, + F: FnMut(Ty<'tcx>) -> Result<(Ty<'tcx>, Option>>), E>, { type BreakTy = OrphanCheckEarlyExit<'tcx, E>; fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow { @@ -858,9 +879,10 @@ where } fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - let ty = match (self.lazily_normalize_ty)(ty) { - Ok(norm_ty) if norm_ty.is_ty_or_numeric_infer() => ty, - Ok(norm_ty) => norm_ty, + let (ty, infcx) = match (self.lazily_normalize_ty)(ty) { + Ok((norm_ty, infcx)) => { + (if norm_ty.is_ty_or_numeric_infer() { ty } else { norm_ty }, infcx) + } Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), }; @@ -880,12 +902,12 @@ where | ty::Tuple(..) => self.found_non_local_ty(ty), ty::Param(..) => match self.in_crate { - InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Local => self.found_uncovered_ty_param(ty, infcx), InCrate::Remote => bug!("unexpected ty param"), }, ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { match self.in_crate { - InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Local => self.found_uncovered_ty_param(ty, infcx), // The inference variable might be unified with a local // type in that remote crate. InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), @@ -895,7 +917,7 @@ where ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) { match self.in_crate { - InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Local => self.found_uncovered_ty_param(ty, infcx), InCrate::Remote => bug!("unexpected ty param"), } } else if ty.has_type_flags( @@ -904,7 +926,7 @@ where | ty::TypeFlags::HAS_TY_INFER, ) { match self.in_crate { - InCrate::Local => self.found_uncovered_ty_param(ty), + InCrate::Local => self.found_uncovered_ty_param(ty, infcx), InCrate::Remote => { // The inference variable might be unified with a local // type in that remote crate. diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr index 3559b226ff738..e931b2559b138 100644 --- a/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr @@ -1,13 +1,12 @@ -error: internal compiler error: compiler/rustc_hir_analysis/src/coherence/orphan.rs:449:40: failed to find ty param in ` as Discard>::Output` +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) + --> $DIR/orphan-check-projections-unsat-bounds.rs:28:6 + | +LL | impl foreign::Trait1 for as Discard>::Output + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last -thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: -Box -stack backtrace: - -query stack during panic: -#0 [orphan_check_impl] checking whether impl `` follows the orphan rules -#1 [coherent_trait] coherence checking all impls of trait `parametrized_trait::Trait1` -#2 [analysis] running analysis passes on this crate -end of query stack error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr index 3559b226ff738..e931b2559b138 100644 --- a/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr @@ -1,13 +1,12 @@ -error: internal compiler error: compiler/rustc_hir_analysis/src/coherence/orphan.rs:449:40: failed to find ty param in ` as Discard>::Output` +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) + --> $DIR/orphan-check-projections-unsat-bounds.rs:28:6 + | +LL | impl foreign::Trait1 for as Discard>::Output + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last -thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: -Box -stack backtrace: - -query stack during panic: -#0 [orphan_check_impl] checking whether impl `` follows the orphan rules -#1 [coherent_trait] coherence checking all impls of trait `parametrized_trait::Trait1` -#2 [analysis] running analysis passes on this crate -end of query stack error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs b/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs index 8f987a98712fe..be62d2a9b90ae 100644 --- a/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs @@ -4,16 +4,6 @@ // Therefore it may fail while normalization outside of orphan checking // succeeds, e.g. due to unsatisfied bounds. -//@ known-bug: unknown -//@ failure-status: 101 -//@ normalize-stderr-test: "DefId\(.*?\]::" -> "DefId(" -//@ normalize-stderr-test: "(?m)note: we would appreciate a bug report.*\n\n" -> "" -//@ normalize-stderr-test: "(?m)note: rustc.*running on.*\n\n" -> "" -//@ normalize-stderr-test: "(?m)note: compiler flags.*\n\n" -> "" -//@ normalize-stderr-test: "(?m)note: delayed at.*$" -> "" -//@ normalize-stderr-test: "(?m)^ *\d+: .*\n" -> "" -//@ normalize-stderr-test: "(?m)^ *at .*\n" -> "" - //@ revisions: classic next //@[next] compile-flags: -Znext-solver @@ -36,6 +26,7 @@ where struct LocalTy; impl foreign::Trait1 for as Discard>::Output +//~^ ERROR type parameter `T` must be covered by another type where Wrapper: Bound {}