Normalize trait ref before orphan check & consider ty params in alias types to be uncovered
This commit is contained in:
parent
c2f2db79ca
commit
951e902562
33 changed files with 1055 additions and 145 deletions
|
@ -486,13 +486,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
|
||||||
|
|
||||||
hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}`
|
hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}`
|
||||||
|
|
||||||
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}`)
|
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_ty}` 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
|
.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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
.case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> 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}>`)
|
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_ty}` must be used as the type parameter for some local type
|
.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
|
.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
|
.only_note = only traits defined in the current crate can be implemented for a type parameter
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,20 @@
|
||||||
//! crate or pertains to a type defined in this crate.
|
//! crate or pertains to a type defined in this crate.
|
||||||
|
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
use rustc_errors::ErrorGuaranteed;
|
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_middle::ty::{self, AliasKind, TyCtxt, TypeVisitableExt};
|
|
||||||
use rustc_span::def_id::LocalDefId;
|
|
||||||
use rustc_span::Span;
|
|
||||||
use rustc_trait_selection::traits::{self, IsFirstInputType};
|
|
||||||
|
|
||||||
#[instrument(skip(tcx), level = "debug")]
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
|
use rustc_errors::ErrorGuaranteed;
|
||||||
|
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||||
|
use rustc_lint_defs::builtin::UNCOVERED_PARAM_IN_PROJECTION;
|
||||||
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
|
use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||||
|
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||||
|
use rustc_span::def_id::{DefId, LocalDefId};
|
||||||
|
use rustc_trait_selection::traits::{self, IsFirstInputType, UncoveredTyParams};
|
||||||
|
use rustc_trait_selection::traits::{OrphanCheckErr, OrphanCheckMode};
|
||||||
|
use rustc_trait_selection::traits::{StructurallyNormalizeExt, TraitEngineExt};
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
pub(crate) fn orphan_check_impl(
|
pub(crate) fn orphan_check_impl(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
impl_def_id: LocalDefId,
|
impl_def_id: LocalDefId,
|
||||||
|
@ -17,30 +23,22 @@ pub(crate) fn orphan_check_impl(
|
||||||
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
|
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
|
||||||
trait_ref.error_reported()?;
|
trait_ref.error_reported()?;
|
||||||
|
|
||||||
let trait_def_id = trait_ref.def_id;
|
match orphan_check(tcx, impl_def_id, OrphanCheckMode::Proper) {
|
||||||
|
|
||||||
match traits::orphan_check(tcx, impl_def_id.to_def_id()) {
|
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(err) => {
|
Err(err) => match orphan_check(tcx, impl_def_id, OrphanCheckMode::Compat) {
|
||||||
let item = tcx.hir().expect_item(impl_def_id);
|
Ok(()) => match err {
|
||||||
let hir::ItemKind::Impl(impl_) = item.kind else {
|
OrphanCheckErr::UncoveredTyParams(uncovered_ty_params) => {
|
||||||
bug!("{:?} is not an impl: {:?}", impl_def_id, item);
|
lint_uncovered_ty_params(tcx, uncovered_ty_params, impl_def_id)
|
||||||
};
|
}
|
||||||
let tr = impl_.of_trait.as_ref().unwrap();
|
OrphanCheckErr::NonLocalInputType(_) => {
|
||||||
let sp = tcx.def_span(impl_def_id);
|
bug!("orphanck: shouldn't've gotten non-local input tys in compat mode")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
emit_orphan_check_error(
|
let trait_def_id = trait_ref.def_id;
|
||||||
tcx,
|
|
||||||
sp,
|
|
||||||
item.span,
|
|
||||||
tr.path.span,
|
|
||||||
trait_ref,
|
|
||||||
impl_.self_ty.span,
|
|
||||||
impl_.generics,
|
|
||||||
err,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In addition to the above rules, we restrict impls of auto traits
|
// In addition to the above rules, we restrict impls of auto traits
|
||||||
// so that they can only be implemented on nominal types, such as structs,
|
// so that they can only be implemented on nominal types, such as structs,
|
||||||
|
@ -186,13 +184,13 @@ pub(crate) fn orphan_check_impl(
|
||||||
// type This = T;
|
// type This = T;
|
||||||
// }
|
// }
|
||||||
// impl<T: ?Sized> AutoTrait for <T as Id>::This {}
|
// impl<T: ?Sized> AutoTrait for <T as Id>::This {}
|
||||||
AliasKind::Projection => "associated type",
|
ty::Projection => "associated type",
|
||||||
// type Foo = (impl Sized, bool)
|
// type Foo = (impl Sized, bool)
|
||||||
// impl AutoTrait for Foo {}
|
// impl AutoTrait for Foo {}
|
||||||
AliasKind::Weak => "type alias",
|
ty::Weak => "type alias",
|
||||||
// type Opaque = impl Trait;
|
// type Opaque = impl Trait;
|
||||||
// impl AutoTrait for Opaque {}
|
// impl AutoTrait for Opaque {}
|
||||||
AliasKind::Opaque => "opaque type",
|
ty::Opaque => "opaque type",
|
||||||
// ```
|
// ```
|
||||||
// struct S<T>(T);
|
// struct S<T>(T);
|
||||||
// impl<T: ?Sized> S<T> {
|
// impl<T: ?Sized> S<T> {
|
||||||
|
@ -201,7 +199,7 @@ pub(crate) fn orphan_check_impl(
|
||||||
// impl<T: ?Sized> AutoTrait for S<T>::This {}
|
// impl<T: ?Sized> AutoTrait for S<T>::This {}
|
||||||
// ```
|
// ```
|
||||||
// FIXME(inherent_associated_types): The example code above currently leads to a cycle
|
// FIXME(inherent_associated_types): The example code above currently leads to a cycle
|
||||||
AliasKind::Inherent => "associated type",
|
ty::Inherent => "associated type",
|
||||||
};
|
};
|
||||||
(LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
|
(LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
|
||||||
}
|
}
|
||||||
|
@ -275,34 +273,125 @@ pub(crate) fn orphan_check_impl(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the coherence orphan rules.
|
||||||
|
///
|
||||||
|
/// `impl_def_id` should be the `DefId` of a trait impl.
|
||||||
|
///
|
||||||
|
/// To pass, either the trait must be local, or else two conditions must be satisfied:
|
||||||
|
///
|
||||||
|
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
|
||||||
|
/// 2. Some local type must appear in `Self`.
|
||||||
|
#[instrument(level = "debug", skip(tcx), ret)]
|
||||||
|
fn orphan_check<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
impl_def_id: LocalDefId,
|
||||||
|
mode: OrphanCheckMode,
|
||||||
|
) -> Result<(), OrphanCheckErr<'tcx, FxIndexSet<DefId>>> {
|
||||||
|
// 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();
|
||||||
|
debug!(trait_ref = ?trait_ref.skip_binder());
|
||||||
|
|
||||||
|
// If the *trait* is local to the crate, ok.
|
||||||
|
if let Some(def_id) = trait_ref.skip_binder().def_id.as_local() {
|
||||||
|
debug!("trait {def_id:?} is local to current crate");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// (1) Instantiate all generic params with fresh inference vars.
|
||||||
|
let infcx = tcx.infer_ctxt().intercrate(true).build();
|
||||||
|
let cause = traits::ObligationCause::dummy();
|
||||||
|
let args = infcx.fresh_args_for_item(cause.span, impl_def_id.to_def_id());
|
||||||
|
let trait_ref = trait_ref.instantiate(tcx, args);
|
||||||
|
|
||||||
|
let lazily_normalize_ty = |user_ty: Ty<'tcx>| {
|
||||||
|
let ty::Alias(..) = user_ty.kind() else { return Ok(user_ty) };
|
||||||
|
|
||||||
|
let ocx = traits::ObligationCtxt::new(&infcx);
|
||||||
|
let ty = ocx.normalize(&cause, ty::ParamEnv::empty(), user_ty);
|
||||||
|
let ty = infcx.resolve_vars_if_possible(ty);
|
||||||
|
let errors = ocx.select_where_possible();
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Ok(user_ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = if infcx.next_trait_solver() {
|
||||||
|
let mut fulfill_cx = <dyn traits::TraitEngine<'_>>::new(&infcx);
|
||||||
|
infcx
|
||||||
|
.at(&cause, ty::ParamEnv::empty())
|
||||||
|
.structurally_normalize(ty, &mut *fulfill_cx)
|
||||||
|
.map(|ty| infcx.resolve_vars_if_possible(ty))
|
||||||
|
.unwrap_or(ty)
|
||||||
|
} else {
|
||||||
|
ty
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ty)
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(result) = traits::orphan_check_trait_ref::<!>(
|
||||||
|
trait_ref,
|
||||||
|
traits::InCrate::Local { mode },
|
||||||
|
lazily_normalize_ty,
|
||||||
|
) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
// (2) Try to map the remaining inference vars back to generic params.
|
||||||
|
result.map_err(|err| match err {
|
||||||
|
OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
|
||||||
|
let mut collector =
|
||||||
|
UncoveredTyParamCollector { infcx: &infcx, uncovered_params: Default::default() };
|
||||||
|
uncovered.visit_with(&mut collector);
|
||||||
|
// FIXME(fmease): This is very likely reachable.
|
||||||
|
debug_assert!(!collector.uncovered_params.is_empty());
|
||||||
|
|
||||||
|
OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
|
||||||
|
uncovered: collector.uncovered_params,
|
||||||
|
local_ty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
OrphanCheckErr::NonLocalInputType(tys) => {
|
||||||
|
let generics = tcx.generics_of(impl_def_id);
|
||||||
|
let tys = tys
|
||||||
|
.into_iter()
|
||||||
|
.map(|(ty, is_target_ty)| {
|
||||||
|
(ty.fold_with(&mut TyVarReplacer { infcx: &infcx, generics }), is_target_ty)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
OrphanCheckErr::NonLocalInputType(tys)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_orphan_check_error<'tcx>(
|
fn emit_orphan_check_error<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
sp: Span,
|
|
||||||
full_impl_span: Span,
|
|
||||||
trait_span: Span,
|
|
||||||
trait_ref: ty::TraitRef<'tcx>,
|
trait_ref: ty::TraitRef<'tcx>,
|
||||||
self_ty_span: Span,
|
impl_def_id: LocalDefId,
|
||||||
generics: &hir::Generics<'tcx>,
|
err: traits::OrphanCheckErr<'tcx, FxIndexSet<DefId>>,
|
||||||
err: traits::OrphanCheckErr<'tcx>,
|
) -> ErrorGuaranteed {
|
||||||
) -> Result<!, ErrorGuaranteed> {
|
match err {
|
||||||
let self_ty = trait_ref.self_ty();
|
|
||||||
Err(match err {
|
|
||||||
traits::OrphanCheckErr::NonLocalInputType(tys) => {
|
traits::OrphanCheckErr::NonLocalInputType(tys) => {
|
||||||
let mut diag = tcx.dcx().create_err(match self_ty.kind() {
|
let item = tcx.hir().expect_item(impl_def_id);
|
||||||
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span: sp, note: () },
|
let impl_ = item.expect_impl();
|
||||||
_ if self_ty.is_primitive() => {
|
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
|
||||||
errors::OnlyCurrentTraits::Primitive { span: sp, note: () }
|
|
||||||
|
let span = tcx.def_span(impl_def_id);
|
||||||
|
let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() {
|
||||||
|
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span, note: () },
|
||||||
|
_ if trait_ref.self_ty().is_primitive() => {
|
||||||
|
errors::OnlyCurrentTraits::Primitive { span, note: () }
|
||||||
}
|
}
|
||||||
_ => errors::OnlyCurrentTraits::Arbitrary { span: sp, note: () },
|
_ => errors::OnlyCurrentTraits::Arbitrary { span, note: () },
|
||||||
});
|
});
|
||||||
|
|
||||||
for &(mut ty, is_target_ty) in &tys {
|
for &(mut ty, is_target_ty) in &tys {
|
||||||
let span = if matches!(is_target_ty, IsFirstInputType::Yes) {
|
let span = if matches!(is_target_ty, IsFirstInputType::Yes) {
|
||||||
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
|
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
|
||||||
self_ty_span
|
impl_.self_ty.span
|
||||||
} else {
|
} else {
|
||||||
// Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
|
// Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
|
||||||
trait_span
|
hir_trait_ref.path.span
|
||||||
};
|
};
|
||||||
|
|
||||||
ty = tcx.erase_regions(ty);
|
ty = tcx.erase_regions(ty);
|
||||||
|
@ -354,12 +443,12 @@ fn emit_orphan_check_error<'tcx>(
|
||||||
diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span });
|
diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span });
|
||||||
}
|
}
|
||||||
ty::RawPtr(ptr_ty, mutbl) => {
|
ty::RawPtr(ptr_ty, mutbl) => {
|
||||||
if !self_ty.has_param() {
|
if !trait_ref.self_ty().has_param() {
|
||||||
diag.subdiagnostic(
|
diag.subdiagnostic(
|
||||||
tcx.dcx(),
|
tcx.dcx(),
|
||||||
errors::OnlyCurrentTraitsPointerSugg {
|
errors::OnlyCurrentTraitsPointerSugg {
|
||||||
wrapper_span: self_ty_span,
|
wrapper_span: impl_.self_ty.span,
|
||||||
struct_span: full_impl_span.shrink_to_lo(),
|
struct_span: item.span.shrink_to_lo(),
|
||||||
mut_key: mutbl.prefix_str(),
|
mut_key: mutbl.prefix_str(),
|
||||||
ptr_ty,
|
ptr_ty,
|
||||||
},
|
},
|
||||||
|
@ -387,23 +476,112 @@ fn emit_orphan_check_error<'tcx>(
|
||||||
|
|
||||||
diag.emit()
|
diag.emit()
|
||||||
}
|
}
|
||||||
traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
|
traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
|
||||||
let mut sp = sp;
|
let mut reported = None;
|
||||||
for param in generics.params {
|
for param_def_id in uncovered {
|
||||||
if param.name.ident().to_string() == param_ty.to_string() {
|
let span = tcx.def_ident_span(param_def_id).unwrap();
|
||||||
sp = param.span;
|
let name = tcx.item_name(param_def_id);
|
||||||
|
|
||||||
|
reported.get_or_insert(match local_ty {
|
||||||
|
Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
|
||||||
|
span,
|
||||||
|
note: (),
|
||||||
|
param: name,
|
||||||
|
local_type,
|
||||||
|
}),
|
||||||
|
None => tcx.dcx().emit_err(errors::TyParamSome { span, note: (), param: name }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
reported.unwrap() // FIXME(fmease): This is very likely reachable.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match local_type {
|
fn lint_uncovered_ty_params<'tcx>(
|
||||||
Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
|
tcx: TyCtxt<'tcx>,
|
||||||
span: sp,
|
UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet<DefId>>,
|
||||||
note: (),
|
impl_def_id: LocalDefId,
|
||||||
param_ty,
|
) {
|
||||||
local_type,
|
let hir_id = tcx.local_def_id_to_hir_id(impl_def_id);
|
||||||
}),
|
|
||||||
None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }),
|
for param_def_id in uncovered {
|
||||||
|
let span = tcx.def_ident_span(param_def_id).unwrap();
|
||||||
|
let name = tcx.item_name(param_def_id);
|
||||||
|
|
||||||
|
match local_ty {
|
||||||
|
Some(local_type) => tcx.emit_node_span_lint(
|
||||||
|
UNCOVERED_PARAM_IN_PROJECTION,
|
||||||
|
hir_id,
|
||||||
|
span,
|
||||||
|
errors::TyParamFirstLocalLint { span, note: (), param: name, local_type },
|
||||||
|
),
|
||||||
|
None => tcx.emit_node_span_lint(
|
||||||
|
UNCOVERED_PARAM_IN_PROJECTION,
|
||||||
|
hir_id,
|
||||||
|
span,
|
||||||
|
errors::TyParamSomeLint { span, note: (), param: name },
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
struct UncoveredTyParamCollector<'cx, 'tcx> {
|
||||||
|
infcx: &'cx InferCtxt<'tcx>,
|
||||||
|
uncovered_params: FxIndexSet<DefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyParamCollector<'_, 'tcx> {
|
||||||
|
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||||
|
if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(origin) = self.infcx.type_var_origin(ty) else {
|
||||||
|
return ty.super_visit_with(self);
|
||||||
|
};
|
||||||
|
if let Some(def_id) = origin.param_def_id {
|
||||||
|
self.uncovered_params.insert(def_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
|
||||||
|
if ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
|
||||||
|
ct.super_visit_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TyVarReplacer<'cx, 'tcx> {
|
||||||
|
infcx: &'cx InferCtxt<'tcx>,
|
||||||
|
generics: &'tcx ty::Generics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for TyVarReplacer<'cx, 'tcx> {
|
||||||
|
fn interner(&self) -> TyCtxt<'tcx> {
|
||||||
|
self.infcx.tcx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
|
if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
let Some(origin) = self.infcx.type_var_origin(ty) else {
|
||||||
|
return ty.super_fold_with(self);
|
||||||
|
};
|
||||||
|
if let Some(def_id) = origin.param_def_id {
|
||||||
|
// The generics of an `impl` don't have a parent, we can index directly.
|
||||||
|
let index = self.generics.param_def_id_to_index[&def_id];
|
||||||
|
let name = self.generics.params[index as usize].name;
|
||||||
|
|
||||||
|
Ty::new_param(self.infcx.tcx, index, name)
|
||||||
|
} else {
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
|
if !ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
|
||||||
|
return ct;
|
||||||
|
}
|
||||||
|
ct.super_fold_with(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1355,29 +1355,54 @@ pub struct CrossCrateTraitsDefined {
|
||||||
pub traits: String,
|
pub traits: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME(fmease): Deduplicate:
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_analysis_ty_param_first_local, code = E0210)]
|
#[diag(hir_analysis_ty_param_first_local, code = E0210)]
|
||||||
#[note]
|
#[note]
|
||||||
pub struct TyParamFirstLocal<'a> {
|
pub struct TyParamFirstLocal<'tcx> {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
#[label]
|
#[label]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
#[note(hir_analysis_case_note)]
|
#[note(hir_analysis_case_note)]
|
||||||
pub note: (),
|
pub note: (),
|
||||||
pub param_ty: Ty<'a>,
|
pub param: Symbol,
|
||||||
pub local_type: Ty<'a>,
|
pub local_type: Ty<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(hir_analysis_ty_param_first_local, code = E0210)]
|
||||||
|
#[note]
|
||||||
|
pub struct TyParamFirstLocalLint<'tcx> {
|
||||||
|
#[label]
|
||||||
|
pub span: Span,
|
||||||
|
#[note(hir_analysis_case_note)]
|
||||||
|
pub note: (),
|
||||||
|
pub param: Symbol,
|
||||||
|
pub local_type: Ty<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_analysis_ty_param_some, code = E0210)]
|
#[diag(hir_analysis_ty_param_some, code = E0210)]
|
||||||
#[note]
|
#[note]
|
||||||
pub struct TyParamSome<'a> {
|
pub struct TyParamSome {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
#[label]
|
#[label]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
#[note(hir_analysis_only_note)]
|
#[note(hir_analysis_only_note)]
|
||||||
pub note: (),
|
pub note: (),
|
||||||
pub param_ty: Ty<'a>,
|
pub param: Symbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(hir_analysis_ty_param_some, code = E0210)]
|
||||||
|
#[note]
|
||||||
|
pub struct TyParamSomeLint {
|
||||||
|
#[label]
|
||||||
|
pub span: Span,
|
||||||
|
#[note(hir_analysis_only_note)]
|
||||||
|
pub note: (),
|
||||||
|
pub param: Symbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
|
|
|
@ -101,6 +101,7 @@ declare_lint_pass! {
|
||||||
TYVAR_BEHIND_RAW_POINTER,
|
TYVAR_BEHIND_RAW_POINTER,
|
||||||
UNCONDITIONAL_PANIC,
|
UNCONDITIONAL_PANIC,
|
||||||
UNCONDITIONAL_RECURSION,
|
UNCONDITIONAL_RECURSION,
|
||||||
|
UNCOVERED_PARAM_IN_PROJECTION,
|
||||||
UNDEFINED_NAKED_FUNCTION_ABI,
|
UNDEFINED_NAKED_FUNCTION_ABI,
|
||||||
UNEXPECTED_CFGS,
|
UNEXPECTED_CFGS,
|
||||||
UNFULFILLED_LINT_EXPECTATIONS,
|
UNFULFILLED_LINT_EXPECTATIONS,
|
||||||
|
@ -4741,3 +4742,68 @@ declare_lint! {
|
||||||
};
|
};
|
||||||
crate_level_only
|
crate_level_only
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `uncovered_param_in_projection` lint detects a violation of one of Rust's orphan rules for
|
||||||
|
/// foreign trait implementations that concerns the use of type parameters inside trait associated
|
||||||
|
/// type paths ("projections") whose output may not be a local type that is mistakenly considered
|
||||||
|
/// to "cover" said parameters which is **unsound** and which may be rejected by a future version
|
||||||
|
/// of the compiler.
|
||||||
|
///
|
||||||
|
/// Originally reported in [#99554].
|
||||||
|
///
|
||||||
|
/// [#99554]: https://github.com/rust-lang/rust/issues/99554
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore (dependent)
|
||||||
|
/// // dependency.rs
|
||||||
|
/// #![crate_type = "lib"]
|
||||||
|
///
|
||||||
|
/// pub trait Trait<T, U> {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```edition2021,ignore (needs dependency)
|
||||||
|
/// // dependent.rs
|
||||||
|
/// trait Identity {
|
||||||
|
/// type Output;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl<T> Identity for T {
|
||||||
|
/// type Output = T;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// struct Local;
|
||||||
|
///
|
||||||
|
/// impl<T> dependency::Trait<Local, T> for <T as Identity>::Output {}
|
||||||
|
///
|
||||||
|
/// fn main() {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This will produce:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
/// --> dependent.rs:11:6
|
||||||
|
/// |
|
||||||
|
/// 11 | impl<T> dependency::Trait<Local, T> for <T as Identity>::Output {}
|
||||||
|
/// | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
/// |
|
||||||
|
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
/// = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
/// = 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
/// = note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// FIXME(fmease): Write explainer.
|
||||||
|
pub UNCOVERED_PARAM_IN_PROJECTION,
|
||||||
|
Warn,
|
||||||
|
"impl contains type parameters that are not covered",
|
||||||
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
|
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||||
|
reference: "issue #124559 <https://github.com/rust-lang/rust/issues/124559>",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -20,15 +20,15 @@ use crate::traits::{
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
use rustc_errors::{Diag, EmissionGuarantee};
|
use rustc_errors::{Diag, EmissionGuarantee};
|
||||||
use rustc_hir::def::DefKind;
|
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::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt};
|
use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt};
|
||||||
use rustc_middle::traits::query::NoSolution;
|
use rustc_middle::traits::query::NoSolution;
|
||||||
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
|
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
|
||||||
use rustc_middle::traits::specialization_graph::OverlapMode;
|
use rustc_middle::traits::specialization_graph::OverlapMode;
|
||||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||||
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -36,14 +36,28 @@ use std::ops::ControlFlow;
|
||||||
|
|
||||||
use super::error_reporting::suggest_new_overflow_limit;
|
use super::error_reporting::suggest_new_overflow_limit;
|
||||||
|
|
||||||
/// Whether we do the orphan check relative to this crate or
|
/// Whether we do the orphan check relative to this crate or to some remote crate.
|
||||||
/// to some remote crate.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum InCrate {
|
pub enum InCrate {
|
||||||
Local,
|
Local { mode: OrphanCheckMode },
|
||||||
Remote,
|
Remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum OrphanCheckMode {
|
||||||
|
/// Proper orphan check.
|
||||||
|
Proper,
|
||||||
|
/// Improper orphan check for backward compatibility.
|
||||||
|
///
|
||||||
|
/// In this mode, type params inside projections are considered to be covered
|
||||||
|
/// even if the projection may normalize to a type that doesn't actually cover
|
||||||
|
/// them. This is unsound. See also [#124559] and [#99554].
|
||||||
|
///
|
||||||
|
/// [#124559]: https://github.com/rust-lang/rust/issues/124559
|
||||||
|
/// [#99554]: https://github.com/rust-lang/rust/issues/99554
|
||||||
|
Compat,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Conflict {
|
pub enum Conflict {
|
||||||
Upstream,
|
Upstream,
|
||||||
|
@ -633,7 +647,13 @@ pub fn trait_ref_is_knowable<'tcx, E: Debug>(
|
||||||
// and if we are an intermediate owner, then we don't care
|
// and if we are an intermediate owner, then we don't care
|
||||||
// about future-compatibility, which means that we're OK if
|
// about future-compatibility, which means that we're OK if
|
||||||
// we are an owner.
|
// 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 { mode: OrphanCheckMode::Proper },
|
||||||
|
&mut lazily_normalize_ty,
|
||||||
|
)?
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
Ok(Ok(()))
|
Ok(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
Ok(Err(Conflict::Upstream))
|
Ok(Err(Conflict::Upstream))
|
||||||
|
@ -644,7 +664,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_ref: ty::TraitRef<'tcx>,
|
trait_ref: ty::TraitRef<'tcx>,
|
||||||
) -> bool {
|
) -> 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)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -663,31 +683,15 @@ impl From<bool> for IsFirstInputType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum OrphanCheckErr<'tcx> {
|
pub enum OrphanCheckErr<'tcx, T> {
|
||||||
NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>),
|
NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>),
|
||||||
UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
|
UncoveredTyParams(UncoveredTyParams<'tcx, T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the coherence orphan rules. `impl_def_id` should be the
|
#[derive(Debug)]
|
||||||
/// `DefId` of a trait impl. To pass, either the trait must be local, or else
|
pub struct UncoveredTyParams<'tcx, T> {
|
||||||
/// two conditions must be satisfied:
|
pub uncovered: T,
|
||||||
///
|
pub local_ty: Option<Ty<'tcx>>,
|
||||||
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
|
|
||||||
/// 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
|
|
||||||
// of a trait, not inherent implementations.
|
|
||||||
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
|
|
||||||
debug!(?trait_ref);
|
|
||||||
|
|
||||||
// If the *trait* is local to the crate, ok.
|
|
||||||
if trait_ref.def_id.is_local() {
|
|
||||||
debug!("trait {:?} is local to current crate", trait_ref.def_id);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a trait-ref is potentially implementable by a crate.
|
/// Checks whether a trait-ref is potentially implementable by a crate.
|
||||||
|
@ -735,6 +739,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
|
/// To check that a local impl follows the orphan rules, we check it in
|
||||||
/// InCrate::Local mode, using type parameters for the "generic" types.
|
/// 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
|
/// 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
|
/// write both a conditional blanket impl and a specific impl, we need to
|
||||||
/// make sure they do not overlap. For example, if we write
|
/// make sure they do not overlap. For example, if we write
|
||||||
|
@ -753,6 +760,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
|
/// 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.
|
/// 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
|
/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can
|
||||||
/// add "non-blanket" impls without breaking negative reasoning in dependent
|
/// add "non-blanket" impls without breaking negative reasoning in dependent
|
||||||
/// crates. This is the "rebalancing coherence" (RFC 1023) restriction.
|
/// crates. This is the "rebalancing coherence" (RFC 1023) restriction.
|
||||||
|
@ -777,11 +787,11 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
|
||||||
/// Note that this function is never called for types that have both type
|
/// Note that this function is never called for types that have both type
|
||||||
/// parameters and inference variables.
|
/// parameters and inference variables.
|
||||||
#[instrument(level = "trace", skip(lazily_normalize_ty), ret)]
|
#[instrument(level = "trace", skip(lazily_normalize_ty), ret)]
|
||||||
fn orphan_check_trait_ref<'tcx, E: Debug>(
|
pub fn orphan_check_trait_ref<'tcx, E: Debug>(
|
||||||
trait_ref: ty::TraitRef<'tcx>,
|
trait_ref: ty::TraitRef<'tcx>,
|
||||||
in_crate: InCrate,
|
in_crate: InCrate,
|
||||||
lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
|
lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
|
||||||
) -> Result<Result<(), OrphanCheckErr<'tcx>>, E> {
|
) -> Result<Result<(), OrphanCheckErr<'tcx, Ty<'tcx>>>, E> {
|
||||||
if trait_ref.has_infer() && trait_ref.has_param() {
|
if trait_ref.has_infer() && trait_ref.has_param() {
|
||||||
bug!(
|
bug!(
|
||||||
"can't orphan check a trait ref with both params and inference variables {:?}",
|
"can't orphan check a trait ref with both params and inference variables {:?}",
|
||||||
|
@ -790,21 +800,28 @@ fn orphan_check_trait_ref<'tcx, E: Debug>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty);
|
let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty);
|
||||||
|
|
||||||
|
// Does there exist some local type after the `ParamTy`.
|
||||||
|
let search_first_local_ty = |checker: &mut OrphanChecker<'tcx, _>| {
|
||||||
|
checker.search_first_local_ty = true;
|
||||||
|
match trait_ref.visit_with(checker).break_value() {
|
||||||
|
Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(match trait_ref.visit_with(&mut checker) {
|
Ok(match trait_ref.visit_with(&mut checker) {
|
||||||
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
|
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
|
||||||
ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
|
ControlFlow::Break(residual) => match residual {
|
||||||
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
|
OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err),
|
||||||
// Does there exist some local type after the `ParamTy`.
|
OrphanCheckEarlyExit::UncoveredTyParam(ty) => {
|
||||||
checker.search_first_local_ty = true;
|
Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
|
||||||
if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
|
uncovered: ty,
|
||||||
trait_ref.visit_with(&mut checker).break_value()
|
local_ty: search_first_local_ty(&mut checker),
|
||||||
{
|
}))
|
||||||
Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty)))
|
|
||||||
} else {
|
|
||||||
Err(OrphanCheckErr::UncoveredTy(ty, None))
|
|
||||||
}
|
}
|
||||||
}
|
OrphanCheckEarlyExit::LocalTy(_) => Ok(()),
|
||||||
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,8 +829,7 @@ struct OrphanChecker<'tcx, F> {
|
||||||
in_crate: InCrate,
|
in_crate: InCrate,
|
||||||
in_self_ty: bool,
|
in_self_ty: bool,
|
||||||
lazily_normalize_ty: F,
|
lazily_normalize_ty: F,
|
||||||
/// Ignore orphan check failures and exclusively search for the first
|
/// Ignore orphan check failures and exclusively search for the first local type.
|
||||||
/// local type.
|
|
||||||
search_first_local_ty: bool,
|
search_first_local_ty: bool,
|
||||||
non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>,
|
non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>,
|
||||||
}
|
}
|
||||||
|
@ -837,17 +853,20 @@ where
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
|
fn found_uncovered_ty_param(
|
||||||
|
&mut self,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
|
||||||
if self.search_first_local_ty {
|
if self.search_first_local_ty {
|
||||||
ControlFlow::Continue(())
|
return ControlFlow::Continue(());
|
||||||
} else {
|
|
||||||
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn def_id_is_local(&mut self, def_id: DefId) -> bool {
|
fn def_id_is_local(&mut self, def_id: DefId) -> bool {
|
||||||
match self.in_crate {
|
match self.in_crate {
|
||||||
InCrate::Local => def_id.is_local(),
|
InCrate::Local { .. } => def_id.is_local(),
|
||||||
InCrate::Remote => false,
|
InCrate::Remote => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -855,7 +874,7 @@ where
|
||||||
|
|
||||||
enum OrphanCheckEarlyExit<'tcx, E> {
|
enum OrphanCheckEarlyExit<'tcx, E> {
|
||||||
NormalizationFailure(E),
|
NormalizationFailure(E),
|
||||||
ParamTy(Ty<'tcx>),
|
UncoveredTyParam(Ty<'tcx>),
|
||||||
LocalTy(Ty<'tcx>),
|
LocalTy(Ty<'tcx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,14 +883,15 @@ where
|
||||||
F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
|
F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
|
||||||
{
|
{
|
||||||
type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>;
|
type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>;
|
||||||
|
|
||||||
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result {
|
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result {
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||||
// Need to lazily normalize here in with `-Znext-solver=coherence`.
|
|
||||||
let ty = match (self.lazily_normalize_ty)(ty) {
|
let ty = match (self.lazily_normalize_ty)(ty) {
|
||||||
Ok(ty) => ty,
|
Ok(norm_ty) if norm_ty.is_ty_var() => ty,
|
||||||
|
Ok(norm_ty) => norm_ty,
|
||||||
Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
|
Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -889,19 +909,46 @@ where
|
||||||
| ty::Slice(..)
|
| ty::Slice(..)
|
||||||
| ty::RawPtr(..)
|
| ty::RawPtr(..)
|
||||||
| ty::Never
|
| ty::Never
|
||||||
| ty::Tuple(..)
|
| ty::Tuple(..) => self.found_non_local_ty(ty),
|
||||||
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
|
|
||||||
self.found_non_local_ty(ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Param(..) => self.found_param_ty(ty),
|
ty::Param(..) => bug!("unexpected ty param"),
|
||||||
|
|
||||||
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
|
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => {
|
||||||
InCrate::Local => self.found_non_local_ty(ty),
|
match self.in_crate {
|
||||||
|
InCrate::Local { .. } => self.found_uncovered_ty_param(ty),
|
||||||
// The inference variable might be unified with a local
|
// The inference variable might be unified with a local
|
||||||
// type in that remote crate.
|
// type in that remote crate.
|
||||||
InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
|
InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Alias(kind @ (ty::Projection | ty::Inherent | ty::Weak), ..) => {
|
||||||
|
if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) {
|
||||||
|
bug!("unexpected ty param in alias ty");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 { mode } => match kind {
|
||||||
|
ty::Projection if let OrphanCheckMode::Compat = mode => {
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
_ => 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.
|
// For fundamental types, we just look inside of them.
|
||||||
ty::Ref(_, ty, _) => ty.visit_with(self),
|
ty::Ref(_, ty, _) => ty.visit_with(self),
|
||||||
|
|
|
@ -41,8 +41,9 @@ use rustc_span::Span;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
|
pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapping_impls};
|
||||||
pub use self::coherence::{IsFirstInputType, OrphanCheckErr, OverlapResult};
|
pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams};
|
||||||
|
pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult};
|
||||||
pub use self::engine::{ObligationCtxt, TraitEngineExt};
|
pub use self::engine::{ObligationCtxt, TraitEngineExt};
|
||||||
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
|
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
|
||||||
pub use self::normalize::NormalizeExt;
|
pub use self::normalize::NormalizeExt;
|
||||||
|
|
2
tests/ui/coherence/auxiliary/parametrized-trait.rs
Normal file
2
tests/ui/coherence/auxiliary/parametrized-trait.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub trait Trait0<T, U, V> {}
|
||||||
|
pub trait Trait1<T, U> {}
|
3
tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs
Normal file
3
tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub trait Trait {
|
||||||
|
type Assoc;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
|
||||||
|
--> $DIR/coherence-overlap-unnormalizable-projection-0.rs:27:1
|
||||||
|
|
|
||||||
|
LL | / impl<T> Trait for T
|
||||||
|
LL | | where
|
||||||
|
LL | | T: 'static,
|
||||||
|
LL | | for<'a> T: WithAssoc<'a>,
|
||||||
|
LL | | for<'a> <T as WithAssoc<'a>>::Assoc: WhereBound,
|
||||||
|
| |____________________________________________________- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> Trait for Box<T> {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
|
||||||
|
|
|
||||||
|
= note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
|
||||||
|
= note: downstream crates may implement trait `WhereBound` for type `<std::boxed::Box<_> as WithAssoc<'a>>::Assoc`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -0,0 +1,18 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
|
||||||
|
--> $DIR/coherence-overlap-unnormalizable-projection-0.rs:27:1
|
||||||
|
|
|
||||||
|
LL | / impl<T> Trait for T
|
||||||
|
LL | | where
|
||||||
|
LL | | T: 'static,
|
||||||
|
LL | | for<'a> T: WithAssoc<'a>,
|
||||||
|
LL | | for<'a> <T as WithAssoc<'a>>::Assoc: WhereBound,
|
||||||
|
| |____________________________________________________- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> Trait for Box<T> {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
|
||||||
|
|
|
||||||
|
= note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Regression test for soundness issue #114061:
|
||||||
|
// "Coherence incorrectly considers `unnormalizable_projection: Trait` to not hold even if it could"
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
//@ revisions: classic next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
|
||||||
|
trait WhereBound {}
|
||||||
|
impl WhereBound for () {}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait WithAssoc<'a> {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These two impls of `Trait` overlap:
|
||||||
|
|
||||||
|
pub trait Trait {}
|
||||||
|
impl<T> Trait for T
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
for<'a> T: WithAssoc<'a>,
|
||||||
|
for<'a> <T as WithAssoc<'a>>::Assoc: WhereBound,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Trait for Box<T> {} //~ ERROR conflicting implementations of trait `Trait` for type `Box<_>`
|
||||||
|
|
||||||
|
// A downstream crate could write:
|
||||||
|
//
|
||||||
|
// use upstream::*;
|
||||||
|
//
|
||||||
|
// struct Local;
|
||||||
|
// impl WithAssoc<'_> for Box<Local> {
|
||||||
|
// type Assoc = ();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn impls_trait<T: Trait>() {}
|
||||||
|
//
|
||||||
|
// fn main() {
|
||||||
|
// impls_trait::<Box<Local>>();
|
||||||
|
// }
|
|
@ -0,0 +1,19 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
|
||||||
|
--> $DIR/coherence-overlap-unnormalizable-projection-1.rs:26:1
|
||||||
|
|
|
||||||
|
LL | / impl<T> Trait for T
|
||||||
|
LL | | where
|
||||||
|
LL | | T: 'static,
|
||||||
|
LL | | for<'a> T: WithAssoc<'a>,
|
||||||
|
LL | | for<'a> Box<<T as WithAssoc<'a>>::Assoc>: WhereBound,
|
||||||
|
| |_________________________________________________________- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> Trait for Box<T> {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
|
||||||
|
|
|
||||||
|
= note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
|
||||||
|
= note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<<std::boxed::Box<_> as WithAssoc<'a>>::Assoc>`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -0,0 +1,19 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
|
||||||
|
--> $DIR/coherence-overlap-unnormalizable-projection-1.rs:26:1
|
||||||
|
|
|
||||||
|
LL | / impl<T> Trait for T
|
||||||
|
LL | | where
|
||||||
|
LL | | T: 'static,
|
||||||
|
LL | | for<'a> T: WithAssoc<'a>,
|
||||||
|
LL | | for<'a> Box<<T as WithAssoc<'a>>::Assoc>: WhereBound,
|
||||||
|
| |_________________________________________________________- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> Trait for Box<T> {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
|
||||||
|
|
|
||||||
|
= note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
|
||||||
|
= note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<<std::boxed::Box<_> as WithAssoc<'a>>::Assoc>`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Regression test for soundness issue #114061:
|
||||||
|
// "Coherence incorrectly considers `unnormalizable_projection: Trait` to not hold even if it could"
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
//@ revisions: classic next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
|
||||||
|
pub trait WhereBound {}
|
||||||
|
impl WhereBound for () {}
|
||||||
|
|
||||||
|
pub trait WithAssoc<'a> {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These two impls of `Trait` overlap:
|
||||||
|
|
||||||
|
pub trait Trait {}
|
||||||
|
impl<T> Trait for T
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
for<'a> T: WithAssoc<'a>,
|
||||||
|
for<'a> Box<<T as WithAssoc<'a>>::Assoc>: WhereBound,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Trait for Box<T> {} //~ ERROR conflicting implementations of trait `Trait` for type `Box<_>`
|
||||||
|
|
||||||
|
// A downstream crate could write:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// use upstream::*;
|
||||||
|
//
|
||||||
|
// struct Local;
|
||||||
|
// impl WithAssoc<'_> for Box<Local> {
|
||||||
|
// type Assoc = Local;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl WhereBound for Box<Local> {}
|
||||||
|
//
|
||||||
|
// fn impls_trait<T: Trait>() {}
|
||||||
|
//
|
||||||
|
// fn main() {
|
||||||
|
// impls_trait::<Box<Local>>();
|
||||||
|
// }
|
25
tests/ui/coherence/orphan-check-projections-covering.rs
Normal file
25
tests/ui/coherence/orphan-check-projections-covering.rs
Normal file
|
@ -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<T> Project for Wrapper<T> {
|
||||||
|
type Output = Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wrapper<T>(T);
|
||||||
|
struct Local;
|
||||||
|
|
||||||
|
impl<T> foreign::Trait1<Local, T> for <Wrapper<T> as Project>::Output {}
|
||||||
|
|
||||||
|
fn main() {}
|
23
tests/ui/coherence/orphan-check-projections-nested.rs
Normal file
23
tests/ui/coherence/orphan-check-projections-nested.rs
Normal file
|
@ -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<T> {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Type<T, U, V>(T, U, V);
|
||||||
|
|
||||||
|
impl<T, U> dep::Trait for Type<T, <<T as dep::Trait>::Assoc as Trait<U>>::Assoc, U>
|
||||||
|
where
|
||||||
|
T: dep::Trait,
|
||||||
|
<T as dep::Trait>::Assoc: Trait<U>,
|
||||||
|
{
|
||||||
|
type Assoc = U;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,15 @@
|
||||||
|
warning[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:25:6
|
||||||
|
|
|
||||||
|
LL | impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
|
@ -0,0 +1,15 @@
|
||||||
|
warning[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:25:6
|
||||||
|
|
|
||||||
|
LL | impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
//@ compile-flags: --crate-type=lib
|
||||||
|
//@ aux-crate:foreign=parametrized-trait.rs
|
||||||
|
//@ edition:2021
|
||||||
|
|
||||||
|
trait Project { type Output; }
|
||||||
|
|
||||||
|
impl<T> Project for T {
|
||||||
|
type Output = Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Local;
|
||||||
|
|
||||||
|
impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
|
||||||
|
//~^ WARNING type parameter `T` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,26 @@
|
||||||
|
warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
--> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:6
|
||||||
|
|
|
||||||
|
LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning[E0210]: type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
--> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:9
|
||||||
|
|
|
||||||
|
LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
|
||||||
|
| ^ type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
|
||||||
|
warning: 2 warnings emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
|
@ -0,0 +1,26 @@
|
||||||
|
warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
--> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:6
|
||||||
|
|
|
||||||
|
LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning[E0210]: type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
--> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:9
|
||||||
|
|
|
||||||
|
LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
|
||||||
|
| ^ type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
|
||||||
|
warning: 2 warnings emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
|
@ -0,0 +1,24 @@
|
||||||
|
//@ revisions: classic next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
//@ compile-flags: --crate-type=lib
|
||||||
|
//@ aux-crate:foreign=parametrized-trait.rs
|
||||||
|
//@ edition:2021
|
||||||
|
|
||||||
|
trait Trait<T, U> { type Assoc; }
|
||||||
|
|
||||||
|
impl<T, U> Trait<T, U> for () {
|
||||||
|
type Assoc = LocalTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LocalTy;
|
||||||
|
|
||||||
|
impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
|
||||||
|
//~^ WARNING type parameter `T` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
||||||
|
//~| WARNING type parameter `U` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,37 @@
|
||||||
|
warning[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:22:6
|
||||||
|
|
|
||||||
|
LL | impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning[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:27:6
|
||||||
|
|
|
||||||
|
LL | impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
|
||||||
|
warning[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:40:6
|
||||||
|
|
|
||||||
|
LL | impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
|
||||||
|
warning: 3 warnings emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
|
@ -0,0 +1,37 @@
|
||||||
|
warning[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:22:6
|
||||||
|
|
|
||||||
|
LL | impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning[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:27:6
|
||||||
|
|
|
||||||
|
LL | impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
|
||||||
|
warning[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:40:6
|
||||||
|
|
|
||||||
|
LL | impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
|
||||||
|
warning: 3 warnings emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
42
tests/ui/coherence/orphan-check-projections-not-covering.rs
Normal file
42
tests/ui/coherence/orphan-check-projections-not-covering.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Regression test for issue #99554.
|
||||||
|
// Projections might not cover type parameters.
|
||||||
|
|
||||||
|
//@ revisions: classic next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
//@ compile-flags: --crate-type=lib
|
||||||
|
//@ aux-crate:foreign=parametrized-trait.rs
|
||||||
|
//@ edition:2021
|
||||||
|
|
||||||
|
trait Identity {
|
||||||
|
type Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Identity for T {
|
||||||
|
type Output = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Local;
|
||||||
|
|
||||||
|
impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
|
||||||
|
//~^ WARNING type parameter `T` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
|
||||||
|
//~^ WARNING type parameter `T` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
||||||
|
|
||||||
|
pub trait Deferred {
|
||||||
|
type Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A downstream user could implement
|
||||||
|
//
|
||||||
|
// impl<T> Deferred for Type<T> { type Output = T; }
|
||||||
|
// struct Type<T>(T);
|
||||||
|
//
|
||||||
|
impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
|
||||||
|
//~^ WARNING type parameter `T` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
|
@ -0,0 +1,15 @@
|
||||||
|
warning[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<T> foreign::Trait1<LocalTy, T> for <Wrapper<T> as Discard>::Output
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
|
@ -0,0 +1,15 @@
|
||||||
|
warning[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<T> foreign::Trait1<LocalTy, T> for <Wrapper<T> as Discard>::Output
|
||||||
|
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
|
||||||
|
= 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<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
|
||||||
|
= note: `#[warn(uncovered_param_in_projection)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0210`.
|
35
tests/ui/coherence/orphan-check-projections-unsat-bounds.rs
Normal file
35
tests/ui/coherence/orphan-check-projections-unsat-bounds.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// 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 for example due to
|
||||||
|
// unsatisfied bounds while normalization outside of orphan checking succeeds.
|
||||||
|
|
||||||
|
//@ revisions: classic next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
//@ aux-crate:foreign=parametrized-trait.rs
|
||||||
|
//@ edition:2021
|
||||||
|
|
||||||
|
struct Wrapper<T>(T);
|
||||||
|
|
||||||
|
trait Bound {}
|
||||||
|
|
||||||
|
trait Discard { type Output; }
|
||||||
|
|
||||||
|
impl<T> Discard for Wrapper<T>
|
||||||
|
where
|
||||||
|
Wrapper<T>: Bound
|
||||||
|
{
|
||||||
|
type Output = LocalTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LocalTy;
|
||||||
|
|
||||||
|
impl<T> foreign::Trait1<LocalTy, T> for <Wrapper<T> as Discard>::Output
|
||||||
|
//~^ WARNING type parameter `T` must be covered by another type
|
||||||
|
//~| WARNING this was previously accepted by the compiler
|
||||||
|
where
|
||||||
|
Wrapper<T>: Bound
|
||||||
|
{}
|
||||||
|
|
||||||
|
fn main() {}
|
20
tests/ui/coherence/orphan-check-weak-aliases-covering.rs
Normal file
20
tests/ui/coherence/orphan-check-weak-aliases-covering.rs
Normal file
|
@ -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<T> = LocalWrapper<T>;
|
||||||
|
|
||||||
|
struct Local;
|
||||||
|
struct LocalWrapper<T>(T);
|
||||||
|
|
||||||
|
impl<T> foreign::Trait1<Local, T> for Alias<T> {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -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<T> foreign::Trait1<Local, T> for Identity<T> {}
|
||||||
|
| ^ 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<T1, ..., Tn> 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`.
|
|
@ -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<T> foreign::Trait1<Local, T> for Identity<T> {}
|
||||||
|
| ^ 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<T1, ..., Tn> 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`.
|
19
tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs
Normal file
19
tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs
Normal file
|
@ -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> = T;
|
||||||
|
|
||||||
|
struct Local;
|
||||||
|
|
||||||
|
impl<T> foreign::Trait1<Local, T> for Identity<T> {}
|
||||||
|
//~^ ERROR type parameter `T` must be covered by another type
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -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<()> {}
|
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
|
| impl doesn't use only types from inside the current crate
|
||||||
|
|
|
|
||||||
= note: define and implement a trait or new type instead
|
= note: define and implement a trait or new type instead
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue