Skip to content

Commit 878f995

Browse files
committed
Normalize trait ref before orphan check
1 parent 9ab0749 commit 878f995

14 files changed

+275
-29
lines changed

compiler/rustc_hir_analysis/messages.ftl

+4-4
Original file line numberDiff line numberDiff line change
@@ -370,13 +370,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
370370
.label = needs at most one field with non-trivial size or alignment, but has {$field_count}
371371
.labels = this field has non-zero size or requires alignment
372372
373-
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}`)
374-
.label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
373+
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}`)
374+
.label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)
375375
.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
376376
.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
377377
378-
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}>`)
379-
.label = type parameter `{$param_ty}` must be used as the type parameter for some local type
378+
hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)
379+
.label = type parameter `{$param}` must be used as the type parameter for some local type
380380
.note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
381381
.only_note = only traits defined in the current crate can be implemented for a type parameter
382382

compiler/rustc_hir_analysis/src/coherence/orphan.rs

+37-10
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};
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,22 +424,49 @@ fn emit_orphan_check_error<'tcx>(
424424
};
425425
tcx.sess.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;
427+
traits::OrphanCheckErr::UncoveredTy(uncovered_ty, local_type) => {
428+
struct TyParamFinder {
429+
ty_params: FxHashMap<Symbol, Span>,
430+
}
431+
432+
// FIXME: This only reports the first uncovered type parameter it finds when in fact
433+
// there could be multiple. E.g., in `<Type<T, U> as Trait>::Assoc` for `<T, U>`.
434+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TyParamFinder {
435+
type BreakTy = (Symbol, Span);
436+
437+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
438+
// FIXME: This doesn't respect macro hygiene.
439+
if let ty::Param(param_ty) = ty.kind()
440+
&& let Some(&span) = self.ty_params.get(&param_ty.name)
441+
{
442+
return ControlFlow::Break((param_ty.name, span));
443+
}
444+
445+
ty.super_visit_with(self)
432446
}
433447
}
434448

449+
let mut visitor = TyParamFinder {
450+
ty_params: generics
451+
.params
452+
.iter()
453+
.filter(|param| matches!(param.kind, hir::GenericParamKind::Type { .. }))
454+
.map(|param| (param.name.ident().name, param.span))
455+
.collect(),
456+
};
457+
458+
let ControlFlow::Break((param, span)) = uncovered_ty.visit_with(&mut visitor) else {
459+
bug!("failed to find ty param in {uncovered_ty}");
460+
};
461+
435462
match local_type {
436463
Some(local_type) => tcx.sess.emit_err(errors::TyParamFirstLocal {
437-
span: sp,
464+
span,
438465
note: (),
439-
param_ty,
466+
param,
440467
local_type,
441468
}),
442-
None => tcx.sess.emit_err(errors::TyParamSome { span: sp, note: (), param_ty }),
469+
None => tcx.sess.emit_err(errors::TyParamSome { span, note: (), param }),
443470
}
444471
}
445472
})

compiler/rustc_hir_analysis/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1198,20 +1198,20 @@ pub struct TyParamFirstLocal<'a> {
11981198
pub span: Span,
11991199
#[note(hir_analysis_case_note)]
12001200
pub note: (),
1201-
pub param_ty: Ty<'a>,
1201+
pub param: Symbol,
12021202
pub local_type: Ty<'a>,
12031203
}
12041204

12051205
#[derive(Diagnostic)]
12061206
#[diag(hir_analysis_ty_param_some, code = "E0210")]
12071207
#[note]
1208-
pub struct TyParamSome<'a> {
1208+
pub struct TyParamSome {
12091209
#[primary_span]
12101210
#[label]
12111211
pub span: Span,
12121212
#[note(hir_analysis_only_note)]
12131213
pub note: (),
1214-
pub param_ty: Ty<'a>,
1214+
pub param: Symbol,
12151215
}
12161216

12171217
#[derive(Diagnostic)]

compiler/rustc_trait_selection/src/traits/coherence.rs

+49-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::traits::{
2020
};
2121
use rustc_data_structures::fx::FxIndexSet;
2222
use rustc_errors::Diagnostic;
23-
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
23+
use rustc_hir::def_id::DefId;
2424
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
2525
use rustc_infer::traits::{util, TraitEngine};
2626
use rustc_middle::traits::query::NoSolution;
@@ -620,7 +620,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>(
620620
tcx: TyCtxt<'tcx>,
621621
trait_ref: ty::TraitRef<'tcx>,
622622
) -> bool {
623-
trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
623+
trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental)
624624
}
625625

626626
#[derive(Debug)]
@@ -637,7 +637,7 @@ pub enum OrphanCheckErr<'tcx> {
637637
/// 2. Some local type must appear in `Self`.
638638
#[instrument(level = "debug", skip(tcx), ret)]
639639
pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
640-
// We only except this routine to be invoked on implementations
640+
// We only accept this routine to be invoked on implementations
641641
// of a trait, not inherent implementations.
642642
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
643643
debug!(?trait_ref);
@@ -648,7 +648,42 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
648648
return Ok(());
649649
}
650650

651-
orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
651+
let delay_bug = || {
652+
tcx.sess.delay_span_bug(
653+
tcx.def_span(impl_def_id),
654+
format!(
655+
"orphan check: failed to normalize `{trait_ref}` while checking {impl_def_id:?}"
656+
),
657+
)
658+
};
659+
660+
let infcx = tcx.infer_ctxt().intercrate(true).build();
661+
let cause = ObligationCause::dummy();
662+
let param_env = tcx.param_env(impl_def_id);
663+
664+
let ocx = ObligationCtxt::new(&infcx);
665+
let trait_ref = ocx.normalize(&cause, param_env, trait_ref);
666+
let trait_ref = infcx.resolve_vars_if_possible(trait_ref);
667+
if !ocx.select_where_possible().is_empty() {
668+
delay_bug();
669+
}
670+
671+
orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| {
672+
Ok(if infcx.next_trait_solver() && let ty::Alias(..) = ty.kind() {
673+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(&infcx);
674+
// FIXME(-Ztrait-solver=next): This normalizes unnormalizable projections to
675+
// infer vars (which can't be resolved here yet) (in intercrate mode) and it
676+
// instantiates ty params to infer vars. This prevents us from syntactically
677+
// checking if the result is a projection that contains ty params.
678+
match infcx.at(&cause, param_env).structurally_normalize(ty, &mut *fulfill_cx) {
679+
Ok(ty) => ty,
680+
_ => Ty::new_error(tcx, delay_bug()),
681+
}
682+
} else {
683+
ty
684+
})
685+
})
686+
.unwrap()
652687
}
653688

654689
/// Checks whether a trait-ref is potentially implementable by a crate.
@@ -754,7 +789,7 @@ fn orphan_check_trait_ref<'tcx, E: Debug>(
754789
Ok(match trait_ref.visit_with(&mut checker) {
755790
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
756791
ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
757-
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
792+
ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTy(ty)) => {
758793
// Does there exist some local type after the `ParamTy`.
759794
checker.search_first_local_ty = true;
760795
if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
@@ -798,11 +833,11 @@ where
798833
ControlFlow::Continue(())
799834
}
800835

801-
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
836+
fn found_uncovered_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
802837
if self.search_first_local_ty {
803838
ControlFlow::Continue(())
804839
} else {
805-
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
840+
ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTy(t))
806841
}
807842
}
808843

@@ -816,7 +851,7 @@ where
816851

817852
enum OrphanCheckEarlyExit<'tcx, E> {
818853
NormalizationFailure(E),
819-
ParamTy(Ty<'tcx>),
854+
UncoveredTy(Ty<'tcx>),
820855
LocalTy(Ty<'tcx>),
821856
}
822857

@@ -851,10 +886,14 @@ where
851886
| ty::Never
852887
| ty::Tuple(..)
853888
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
854-
self.found_non_local_ty(ty)
889+
if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) {
890+
self.found_uncovered_ty(ty)
891+
} else {
892+
ControlFlow::Continue(())
893+
}
855894
}
856895

857-
ty::Param(..) => self.found_param_ty(ty),
896+
ty::Param(..) => self.found_uncovered_ty(ty),
858897

859898
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
860899
InCrate::Local => self.found_non_local_ty(ty),

tests/ui/associated-types/issue-38821.rs

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub trait Column: Expression {}
2222

2323
#[derive(Debug, Copy, Clone)]
2424
//~^ ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
25+
//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
26+
//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
2527
pub enum ColumnInsertValue<Col, Expr> where
2628
Col: Column,
2729
Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,

tests/ui/associated-types/issue-38821.stderr

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
2+
--> $DIR/issue-38821.rs:23:10
3+
|
4+
LL | #[derive(Debug, Copy, Clone)]
5+
| ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType`
6+
|
7+
note: required for `<Col as Expression>::SqlType` to implement `IntoNullable`
8+
--> $DIR/issue-38821.rs:9:18
9+
|
10+
LL | impl<T: NotNull> IntoNullable for T {
11+
| ------- ^^^^^^^^^^^^ ^
12+
| |
13+
| unsatisfied trait bound introduced here
14+
= note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
help: consider further restricting the associated type
16+
|
17+
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
18+
| +++++++++++++++++++++++++++++++++++++++
19+
120
error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
221
--> $DIR/issue-38821.rs:23:17
322
|
@@ -17,6 +36,25 @@ help: consider further restricting the associated type
1736
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
1837
| +++++++++++++++++++++++++++++++++++++++
1938

20-
error: aborting due to previous error
39+
error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
40+
--> $DIR/issue-38821.rs:23:23
41+
|
42+
LL | #[derive(Debug, Copy, Clone)]
43+
| ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType`
44+
|
45+
note: required for `<Col as Expression>::SqlType` to implement `IntoNullable`
46+
--> $DIR/issue-38821.rs:9:18
47+
|
48+
LL | impl<T: NotNull> IntoNullable for T {
49+
| ------- ^^^^^^^^^^^^ ^
50+
| |
51+
| unsatisfied trait bound introduced here
52+
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
53+
help: consider further restricting the associated type
54+
|
55+
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
56+
| +++++++++++++++++++++++++++++++++++++++
57+
58+
error: aborting due to 3 previous errors
2159

2260
For more information about this error, try `rustc --explain E0277`.
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> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
// check-pass
8+
// revisions: classic next
9+
//[next] compile-flags: -Ztrait-solver=next
10+
11+
// aux-crate:foreign=parametrized-trait.rs
12+
// edition:2021
13+
14+
trait Project { type Output; }
15+
16+
impl<T> Project for T {
17+
type Output = Local;
18+
}
19+
20+
struct Local;
21+
22+
impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
23+
24+
fn main() {}
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:25: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:28: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:40: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`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Regression test for issue #99554.
2+
// Projections might not cover type parameters.
3+
4+
// revisions: classic next
5+
//[next] compile-flags: -Ztrait-solver=next
6+
7+
// FIXME(-Ztrait-solver=next): This currently passes in the next solver but it shouldn't.
8+
//[next] check-pass
9+
//[next] known-bug: unknown
10+
11+
// compile-flags: --crate-type=lib
12+
// aux-crate:foreign=parametrized-trait.rs
13+
// edition:2021
14+
15+
trait Identity {
16+
type Output;
17+
}
18+
19+
impl<T> Identity for T {
20+
type Output = T;
21+
}
22+
23+
struct Local;
24+
25+
impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
26+
//[classic]~^ ERROR type parameter `T` must be covered by another type
27+
28+
impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
29+
//[classic]~^ ERROR type parameter `T` must be covered by another type
30+
31+
pub trait Deferred {
32+
type Output;
33+
}
34+
35+
// A downstream user could implement
36+
//
37+
// impl<T> Deferred for Type<T> { type Output = T; }
38+
// struct Type<T>(T);
39+
//
40+
impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
41+
//[classic]~^ ERROR type parameter `T` must be covered by another type

0 commit comments

Comments
 (0)