Auto merge of #117164 - fmease:orphan-norm, r=lcnr
Lazily normalize inside trait ref during orphan check & consider ty params in rigid alias types to be uncovered Fixes #99554, fixes rust-lang/types-team#104. Fixes #114061. Supersedes #100555. Tracking issue for the future compatibility lint: #124559. r? lcnr
This commit is contained in:
commit
f705de5962
@ -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_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<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}>`)
|
||||
.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
|
||||
|
||||
|
@ -2,14 +2,20 @@
|
||||
//! crate or pertains to a type defined in this crate.
|
||||
|
||||
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(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_def_id: LocalDefId,
|
||||
@ -17,31 +23,23 @@ pub(crate) fn orphan_check_impl(
|
||||
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
|
||||
trait_ref.error_reported()?;
|
||||
|
||||
let trait_def_id = trait_ref.def_id;
|
||||
|
||||
match traits::orphan_check(tcx, impl_def_id.to_def_id()) {
|
||||
match orphan_check(tcx, impl_def_id, OrphanCheckMode::Proper) {
|
||||
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);
|
||||
|
||||
emit_orphan_check_error(
|
||||
tcx,
|
||||
sp,
|
||||
item.span,
|
||||
tr.path.span,
|
||||
trait_ref,
|
||||
impl_.self_ty.span,
|
||||
impl_.generics,
|
||||
err,
|
||||
)?
|
||||
}
|
||||
Err(err) => match orphan_check(tcx, impl_def_id, OrphanCheckMode::Compat) {
|
||||
Ok(()) => match err {
|
||||
OrphanCheckErr::UncoveredTyParams(uncovered_ty_params) => {
|
||||
lint_uncovered_ty_params(tcx, uncovered_ty_params, impl_def_id)
|
||||
}
|
||||
OrphanCheckErr::NonLocalInputType(_) => {
|
||||
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)),
|
||||
},
|
||||
}
|
||||
|
||||
let trait_def_id = trait_ref.def_id;
|
||||
|
||||
// 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,
|
||||
// enums or foreign types. To see why this restriction exists, consider the
|
||||
@ -186,13 +184,13 @@ enum NonlocalImpl {
|
||||
// type This = T;
|
||||
// }
|
||||
// impl<T: ?Sized> AutoTrait for <T as Id>::This {}
|
||||
AliasKind::Projection => "associated type",
|
||||
ty::Projection => "associated type",
|
||||
// type Foo = (impl Sized, bool)
|
||||
// impl AutoTrait for Foo {}
|
||||
AliasKind::Weak => "type alias",
|
||||
ty::Weak => "type alias",
|
||||
// type Opaque = impl Trait;
|
||||
// impl AutoTrait for Opaque {}
|
||||
AliasKind::Opaque => "opaque type",
|
||||
ty::Opaque => "opaque type",
|
||||
// ```
|
||||
// struct S<T>(T);
|
||||
// impl<T: ?Sized> S<T> {
|
||||
@ -201,7 +199,7 @@ enum NonlocalImpl {
|
||||
// impl<T: ?Sized> AutoTrait for S<T>::This {}
|
||||
// ```
|
||||
// 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)
|
||||
}
|
||||
@ -275,34 +273,125 @@ enum NonlocalImpl {
|
||||
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>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sp: Span,
|
||||
full_impl_span: Span,
|
||||
trait_span: Span,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
self_ty_span: Span,
|
||||
generics: &hir::Generics<'tcx>,
|
||||
err: traits::OrphanCheckErr<'tcx>,
|
||||
) -> Result<!, ErrorGuaranteed> {
|
||||
let self_ty = trait_ref.self_ty();
|
||||
Err(match err {
|
||||
impl_def_id: LocalDefId,
|
||||
err: traits::OrphanCheckErr<'tcx, FxIndexSet<DefId>>,
|
||||
) -> ErrorGuaranteed {
|
||||
match err {
|
||||
traits::OrphanCheckErr::NonLocalInputType(tys) => {
|
||||
let mut diag = tcx.dcx().create_err(match self_ty.kind() {
|
||||
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span: sp, note: () },
|
||||
_ if self_ty.is_primitive() => {
|
||||
errors::OnlyCurrentTraits::Primitive { span: sp, note: () }
|
||||
let item = tcx.hir().expect_item(impl_def_id);
|
||||
let impl_ = item.expect_impl();
|
||||
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
|
||||
|
||||
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 {
|
||||
let span = if matches!(is_target_ty, IsFirstInputType::Yes) {
|
||||
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
|
||||
self_ty_span
|
||||
impl_.self_ty.span
|
||||
} else {
|
||||
// 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);
|
||||
@ -354,12 +443,12 @@ fn emit_orphan_check_error<'tcx>(
|
||||
diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span });
|
||||
}
|
||||
ty::RawPtr(ptr_ty, mutbl) => {
|
||||
if !self_ty.has_param() {
|
||||
if !trait_ref.self_ty().has_param() {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsPointerSugg {
|
||||
wrapper_span: self_ty_span,
|
||||
struct_span: full_impl_span.shrink_to_lo(),
|
||||
wrapper_span: impl_.self_ty.span,
|
||||
struct_span: item.span.shrink_to_lo(),
|
||||
mut_key: mutbl.prefix_str(),
|
||||
ptr_ty,
|
||||
},
|
||||
@ -387,23 +476,112 @@ fn emit_orphan_check_error<'tcx>(
|
||||
|
||||
diag.emit()
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
|
||||
let mut reported = None;
|
||||
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_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 }),
|
||||
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.
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_uncovered_ty_params<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet<DefId>>,
|
||||
impl_def_id: LocalDefId,
|
||||
) {
|
||||
let hir_id = tcx.local_def_id_to_hir_id(impl_def_id);
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
// FIXME(fmease): Deduplicate:
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_ty_param_first_local, code = E0210)]
|
||||
#[note]
|
||||
pub struct TyParamFirstLocal<'a> {
|
||||
pub struct TyParamFirstLocal<'tcx> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[note(hir_analysis_case_note)]
|
||||
pub note: (),
|
||||
pub param_ty: Ty<'a>,
|
||||
pub local_type: Ty<'a>,
|
||||
pub param: Symbol,
|
||||
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)]
|
||||
#[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(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)]
|
||||
|
@ -101,6 +101,7 @@
|
||||
TYVAR_BEHIND_RAW_POINTER,
|
||||
UNCONDITIONAL_PANIC,
|
||||
UNCONDITIONAL_RECURSION,
|
||||
UNCOVERED_PARAM_IN_PROJECTION,
|
||||
UNDEFINED_NAKED_FUNCTION_ABI,
|
||||
UNEXPECTED_CFGS,
|
||||
UNFULFILLED_LINT_EXPECTATIONS,
|
||||
@ -4741,3 +4742,68 @@
|
||||
};
|
||||
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 rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Diag, 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, FulfillmentErrorCode, TraitEngine, TraitEngineExt};
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
|
||||
use rustc_middle::traits::specialization_graph::OverlapMode;
|
||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
|
||||
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use std::fmt::Debug;
|
||||
@ -36,14 +36,28 @@
|
||||
|
||||
use super::error_reporting::suggest_new_overflow_limit;
|
||||
|
||||
/// Whether we do the orphan check relative to this crate or
|
||||
/// to some remote crate.
|
||||
/// Whether we do the orphan check relative to this crate or to some remote crate.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum InCrate {
|
||||
Local,
|
||||
pub enum InCrate {
|
||||
Local { mode: OrphanCheckMode },
|
||||
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)]
|
||||
pub enum Conflict {
|
||||
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
|
||||
// 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 { mode: OrphanCheckMode::Proper },
|
||||
&mut lazily_normalize_ty,
|
||||
)?
|
||||
.is_ok()
|
||||
{
|
||||
Ok(Ok(()))
|
||||
} else {
|
||||
Ok(Err(Conflict::Upstream))
|
||||
@ -644,7 +664,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)]
|
||||
@ -663,31 +683,15 @@ fn from(b: bool) -> IsFirstInputType {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OrphanCheckErr<'tcx> {
|
||||
pub enum OrphanCheckErr<'tcx, T> {
|
||||
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
|
||||
/// `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)]
|
||||
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()
|
||||
#[derive(Debug)]
|
||||
pub struct UncoveredTyParams<'tcx, T> {
|
||||
pub uncovered: T,
|
||||
pub local_ty: Option<Ty<'tcx>>,
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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
|
||||
@ -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
|
||||
/// 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.
|
||||
@ -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
|
||||
/// parameters and inference variables.
|
||||
#[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>,
|
||||
in_crate: InCrate,
|
||||
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() {
|
||||
bug!(
|
||||
"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);
|
||||
|
||||
// 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) {
|
||||
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
|
||||
ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
|
||||
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(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))
|
||||
ControlFlow::Break(residual) => match residual {
|
||||
OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err),
|
||||
OrphanCheckEarlyExit::UncoveredTyParam(ty) => {
|
||||
Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
|
||||
uncovered: ty,
|
||||
local_ty: search_first_local_ty(&mut checker),
|
||||
}))
|
||||
}
|
||||
}
|
||||
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
|
||||
OrphanCheckEarlyExit::LocalTy(_) => Ok(()),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -812,8 +829,7 @@ struct OrphanChecker<'tcx, F> {
|
||||
in_crate: InCrate,
|
||||
in_self_ty: bool,
|
||||
lazily_normalize_ty: F,
|
||||
/// Ignore orphan check failures and exclusively search for the first
|
||||
/// local type.
|
||||
/// Ignore orphan check failures and exclusively search for the first local type.
|
||||
search_first_local_ty: bool,
|
||||
non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>,
|
||||
}
|
||||
@ -837,17 +853,20 @@ fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExi
|
||||
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 {
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty))
|
||||
}
|
||||
|
||||
fn def_id_is_local(&mut self, def_id: DefId) -> bool {
|
||||
match self.in_crate {
|
||||
InCrate::Local => def_id.is_local(),
|
||||
InCrate::Local { .. } => def_id.is_local(),
|
||||
InCrate::Remote => false,
|
||||
}
|
||||
}
|
||||
@ -855,7 +874,7 @@ fn def_id_is_local(&mut self, def_id: DefId) -> bool {
|
||||
|
||||
enum OrphanCheckEarlyExit<'tcx, E> {
|
||||
NormalizationFailure(E),
|
||||
ParamTy(Ty<'tcx>),
|
||||
UncoveredTyParam(Ty<'tcx>),
|
||||
LocalTy(Ty<'tcx>),
|
||||
}
|
||||
|
||||
@ -864,14 +883,15 @@ impl<'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx, F>
|
||||
F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
|
||||
{
|
||||
type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>;
|
||||
|
||||
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
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) {
|
||||
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)),
|
||||
};
|
||||
|
||||
@ -889,19 +909,46 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
| ty::Slice(..)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..)
|
||||
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
|
||||
self.found_non_local_ty(ty)
|
||||
| ty::Tuple(..) => self.found_non_local_ty(ty),
|
||||
|
||||
ty::Param(..) => 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::Param(..) => self.found_param_ty(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");
|
||||
}
|
||||
|
||||
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)),
|
||||
},
|
||||
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.
|
||||
ty::Ref(_, ty, _) => ty.visit_with(self),
|
||||
|
@ -41,8 +41,9 @@
|
||||
use std::fmt::Debug;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
|
||||
pub use self::coherence::{IsFirstInputType, OrphanCheckErr, OverlapResult};
|
||||
pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapping_impls};
|
||||
pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams};
|
||||
pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult};
|
||||
pub use self::engine::{ObligationCtxt, TraitEngineExt};
|
||||
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
|
||||
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<()> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `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
|
||||
|
Loading…
Reference in New Issue
Block a user