Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b24d9ff

Browse files
committed
Auto merge of rust-lang#117164 - fmease:orphan-norm, r=<try>
Normalize trait ref before orphan check & consider ty params in alias types to be uncovered Fixes rust-lang#99554. r? lcnr
2 parents 2457c02 + ec4d94e commit b24d9ff

17 files changed

+327
-38
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
401401
.label = needs at most one field with non-trivial size or alignment, but has {$field_count}
402402
.labels = this field has non-zero size or requires alignment
403403
404-
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}`)
405-
.label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
404+
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}`)
405+
.label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)
406406
.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
407407
.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
408408
409-
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}>`)
410-
.label = type parameter `{$param_ty}` must be used as the type parameter for some local type
409+
hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)
410+
.label = type parameter `{$param}` must be used as the type parameter for some local type
411411
.note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
412412
.only_note = only traits defined in the current crate can be implemented for a type parameter
413413

compiler/rustc_hir_analysis/src/coherence/orphan.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Orphan checker: every impl either implements a trait defined in this
22
//! crate or pertains to a type defined in this crate.
33
4-
use rustc_data_structures::fx::FxHashSet;
4+
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
55
use rustc_errors::{DelayDm, ErrorGuaranteed};
66
use rustc_hir as hir;
77
use rustc_middle::ty::util::CheckRegions;
@@ -12,7 +12,7 @@ use rustc_middle::ty::{
1212
};
1313
use rustc_session::lint;
1414
use rustc_span::def_id::{DefId, LocalDefId};
15-
use rustc_span::Span;
15+
use rustc_span::{Span, Symbol};
1616
use rustc_trait_selection::traits;
1717
use std::ops::ControlFlow;
1818

@@ -424,23 +424,55 @@ fn emit_orphan_check_error<'tcx>(
424424
};
425425
tcx.dcx().emit_err(err_struct)
426426
}
427-
traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
428-
let mut sp = sp;
429-
for param in generics.params {
430-
if param.name.ident().to_string() == param_ty.to_string() {
431-
sp = param.span;
432-
}
427+
traits::OrphanCheckErr::UncoveredTy(uncovered_ty, local_type) => {
428+
let mut collector = UncoveredTyParamCollector {
429+
available_params: generics
430+
.params
431+
.iter()
432+
.filter(|param| matches!(param.kind, hir::GenericParamKind::Type { .. }))
433+
.map(|param| (param.name.ident().name, param.span))
434+
.collect(),
435+
uncovered_params: Default::default(),
436+
};
437+
uncovered_ty.visit_with(&mut collector);
438+
439+
let mut reported = None;
440+
for (param, span) in collector.uncovered_params {
441+
reported.get_or_insert(match local_type {
442+
Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
443+
span,
444+
note: (),
445+
param,
446+
local_type,
447+
}),
448+
None => tcx.dcx().emit_err(errors::TyParamSome { span, note: (), param }),
449+
});
433450
}
434451

435-
match local_type {
436-
Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
437-
span: sp,
438-
note: (),
439-
param_ty,
440-
local_type,
441-
}),
442-
None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }),
452+
struct UncoveredTyParamCollector {
453+
available_params: FxHashMap<Symbol, Span>,
454+
uncovered_params: FxIndexSet<(Symbol, Span)>,
455+
}
456+
457+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyParamCollector {
458+
type BreakTy = !;
459+
460+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
461+
// FIXME: This doesn't respect macro hygiene I think. We can't look up the param
462+
// in the `hir::Generics.params` by its index since the index is relative to the
463+
// `ty::Generics.params`.
464+
if let ty::Param(param) = *ty.kind()
465+
&& let Some(&span) = self.available_params.get(&param.name)
466+
{
467+
self.uncovered_params.insert((param.name, span));
468+
ControlFlow::Continue(())
469+
} else {
470+
ty.super_visit_with(self)
471+
}
472+
}
443473
}
474+
475+
reported.unwrap_or_else(|| bug!("failed to find ty param in `{uncovered_ty}`"))
444476
}
445477
})
446478
}

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,20 +1279,20 @@ pub struct TyParamFirstLocal<'a> {
12791279
pub span: Span,
12801280
#[note(hir_analysis_case_note)]
12811281
pub note: (),
1282-
pub param_ty: Ty<'a>,
1282+
pub param: Symbol,
12831283
pub local_type: Ty<'a>,
12841284
}
12851285

12861286
#[derive(Diagnostic)]
12871287
#[diag(hir_analysis_ty_param_some, code = "E0210")]
12881288
#[note]
1289-
pub struct TyParamSome<'a> {
1289+
pub struct TyParamSome {
12901290
#[primary_span]
12911291
#[label]
12921292
pub span: Span,
12931293
#[note(hir_analysis_only_note)]
12941294
pub note: (),
1295-
pub param_ty: Ty<'a>,
1295+
pub param: Symbol,
12961296
}
12971297

12981298
#[derive(Diagnostic)]

compiler/rustc_trait_selection/src/traits/coherence.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::traits::{
2121
use rustc_data_structures::fx::FxIndexSet;
2222
use rustc_errors::Diagnostic;
2323
use rustc_hir::def::DefKind;
24-
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
24+
use rustc_hir::def_id::DefId;
2525
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
2626
use rustc_infer::traits::{util, TraitEngine};
2727
use rustc_middle::traits::query::NoSolution;
@@ -592,7 +592,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>(
592592
tcx: TyCtxt<'tcx>,
593593
trait_ref: ty::TraitRef<'tcx>,
594594
) -> bool {
595-
trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
595+
trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental)
596596
}
597597

598598
#[derive(Debug)]
@@ -609,7 +609,7 @@ pub enum OrphanCheckErr<'tcx> {
609609
/// 2. Some local type must appear in `Self`.
610610
#[instrument(level = "debug", skip(tcx), ret)]
611611
pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
612-
// We only except this routine to be invoked on implementations
612+
// We only accept this routine to be invoked on implementations
613613
// of a trait, not inherent implementations.
614614
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
615615
debug!(?trait_ref);
@@ -620,7 +620,42 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
620620
return Ok(());
621621
}
622622

623-
orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
623+
orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| {
624+
let ty::Alias(..) = ty.kind() else { return Ok(ty) };
625+
626+
let delay_bug = || {
627+
tcx.dcx().span_delayed_bug(
628+
tcx.def_span(impl_def_id),
629+
format!(
630+
"orphan check: failed to normalize `{trait_ref}` while checking {impl_def_id:?}"
631+
),
632+
)
633+
};
634+
635+
let infcx = tcx.infer_ctxt().intercrate(true).build();
636+
let cause = ObligationCause::dummy();
637+
638+
let ocx = ObligationCtxt::new(&infcx);
639+
let args = infcx.fresh_args_for_item(cause.span, impl_def_id);
640+
let ty = ty::EarlyBinder::bind(ty).instantiate(tcx, args);
641+
let ty = ocx.normalize(&cause, ty::ParamEnv::empty(), ty);
642+
let ty = infcx.resolve_vars_if_possible(ty);
643+
if !ocx.select_where_possible().is_empty() {
644+
delay_bug();
645+
}
646+
647+
if infcx.next_trait_solver() {
648+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(&infcx);
649+
infcx
650+
.at(&cause, ty::ParamEnv::empty())
651+
.structurally_normalize(ty, &mut *fulfill_cx)
652+
.map(|ty| infcx.resolve_vars_if_possible(ty))
653+
.or_else(|_| Ok(Ty::new_error(tcx, delay_bug())))
654+
} else {
655+
Ok(ty)
656+
}
657+
})
658+
.unwrap()
624659
}
625660

626661
/// Checks whether a trait-ref is potentially implementable by a crate.
@@ -726,7 +761,7 @@ fn orphan_check_trait_ref<'tcx, E: Debug>(
726761
Ok(match trait_ref.visit_with(&mut checker) {
727762
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
728763
ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
729-
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
764+
ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTy(ty)) => {
730765
// Does there exist some local type after the `ParamTy`.
731766
checker.search_first_local_ty = true;
732767
if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
@@ -770,11 +805,11 @@ where
770805
ControlFlow::Continue(())
771806
}
772807

773-
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
808+
fn found_uncovered_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
774809
if self.search_first_local_ty {
775810
ControlFlow::Continue(())
776811
} else {
777-
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
812+
ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTy(t))
778813
}
779814
}
780815

@@ -788,7 +823,7 @@ where
788823

789824
enum OrphanCheckEarlyExit<'tcx, E> {
790825
NormalizationFailure(E),
791-
ParamTy(Ty<'tcx>),
826+
UncoveredTy(Ty<'tcx>),
792827
LocalTy(Ty<'tcx>),
793828
}
794829

@@ -802,9 +837,9 @@ where
802837
}
803838

804839
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
805-
// Need to lazily normalize here in with `-Znext-solver=coherence`.
806840
let ty = match (self.lazily_normalize_ty)(ty) {
807-
Ok(ty) => ty,
841+
Ok(norm_ty) if norm_ty.is_ty_or_numeric_infer() => ty,
842+
Ok(norm_ty) => norm_ty,
808843
Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
809844
};
810845

@@ -821,12 +856,17 @@ where
821856
| ty::Slice(..)
822857
| ty::RawPtr(..)
823858
| ty::Never
824-
| ty::Tuple(..)
825-
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
826-
self.found_non_local_ty(ty)
859+
| ty::Tuple(..) => self.found_non_local_ty(ty),
860+
861+
ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
862+
if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) {
863+
self.found_uncovered_ty(ty)
864+
} else {
865+
ControlFlow::Continue(())
866+
}
827867
}
828868

829-
ty::Param(..) => self.found_param_ty(ty),
869+
ty::Param(..) => self.found_uncovered_ty(ty),
830870

831871
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
832872
InCrate::Local => self.found_non_local_ty(ty),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub trait Trait0<T, U, V> {}
2+
pub trait Trait1<T, U> {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub trait Trait {
2+
type Assoc;
3+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// This used to ICE in an earlier iteration of #117164. Minimized from crate `proqnt`.
2+
3+
// check-pass
4+
// revisions: classic next
5+
//[next] compile-flags: -Znext-solver
6+
// aux-crate:dep=trait-with-assoc-ty.rs
7+
// edition: 2021
8+
9+
pub(crate) trait Trait<T> {
10+
type Assoc;
11+
}
12+
13+
pub(crate) struct Type<T, U, V>(T, U, V);
14+
15+
impl<T, U> dep::Trait for Type<T, <<T as dep::Trait>::Assoc as Trait<U>>::Assoc, U>
16+
where
17+
T: dep::Trait,
18+
<T as dep::Trait>::Assoc: Trait<U>,
19+
{
20+
type Assoc = U;
21+
}
22+
23+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
2+
--> $DIR/orphan-check-projection-does-cover.rs:23:6
3+
|
4+
LL | impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
5+
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
6+
|
7+
= 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
8+
= 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
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0210`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
2+
--> $DIR/orphan-check-projection-does-cover.rs:23:6
3+
|
4+
LL | impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
5+
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
6+
|
7+
= 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
8+
= 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
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0210`.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Projections can cover type parameters if they normalize to a (local) type that covers them.
2+
// This ensures that we don't perform an overly strict check on
3+
// projections like in closed PR #100555 which did a syntactic
4+
// check for type parameters in projections without normalizing
5+
// first which would've lead to real-word regressions.
6+
7+
// FIXME: check-pass
8+
// known-bug: unknown
9+
// revisions: classic next
10+
//[next] compile-flags: -Znext-solver
11+
12+
// aux-crate:foreign=parametrized-trait.rs
13+
// edition:2021
14+
15+
trait Project { type Output; }
16+
17+
impl<T> Project for T {
18+
type Output = Local;
19+
}
20+
21+
struct Local;
22+
23+
impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
24+
25+
fn main() {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
2+
--> $DIR/orphan-check-projection-doesnt-cover.rs:21:6
3+
|
4+
LL | impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
5+
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
6+
|
7+
= 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
8+
= 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
9+
10+
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
11+
--> $DIR/orphan-check-projection-doesnt-cover.rs:24:6
12+
|
13+
LL | impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
14+
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
15+
|
16+
= 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
17+
= 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
18+
19+
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
20+
--> $DIR/orphan-check-projection-doesnt-cover.rs:36:6
21+
|
22+
LL | impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
23+
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
24+
|
25+
= 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
26+
= 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
27+
28+
error: aborting due to 3 previous errors
29+
30+
For more information about this error, try `rustc --explain E0210`.

0 commit comments

Comments
 (0)