Skip to content

Commit 2f0cace

Browse files
committed
Auto merge of rust-lang#125782 - compiler-errors:supertrait-item-shadowing, r=<try>
Implement RFC 3624 `supertrait_item_shadowing` (v2) Implements RFC 3624 and the associated lint in the RFC. Implements: * Shadowing algorithm * Lint for call-site shadowing (allow by default, gated) * Lint for definition-site shadowing (allow by default, gated) Tracking: - rust-lang#89151 cc `@Amanieu` and rust-lang/rfcs#3624 and rust-lang#89151
2 parents 203e6c1 + 8409aaa commit 2f0cace

36 files changed

+1056
-1
lines changed

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,8 @@ declare_features! (
618618
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
619619
/// Allows string patterns to dereference values to match them.
620620
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
621+
/// Allows subtrait items to shadow supertrait items.
622+
(unstable, supertrait_item_shadowing, "CURRENT_RUSTC_VERSION", Some(89151)),
621623
/// Allows the use of `#[target_feature]` on safe functions.
622624
(unstable, target_feature_11, "1.45.0", Some(69098)),
623625
/// Allows using `#[thread_local]` on `static` items.

compiler/rustc_hir_analysis/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,12 @@ hir_analysis_specialization_trait = implementing `rustc_specialization_trait` tr
497497
498498
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
499499
500+
hir_analysis_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
501+
502+
hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
503+
504+
hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
505+
500506
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
501507
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
502508

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+43
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
1212
use rustc_hir::{AmbigArg, ItemKind};
1313
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1414
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
15+
use rustc_lint_defs::builtin::SUPERTRAIT_ITEM_SHADOWING_DEFINITION;
1516
use rustc_macros::LintDiagnostic;
1617
use rustc_middle::mir::interpret::ErrorHandled;
1718
use rustc_middle::query::Providers;
@@ -377,7 +378,10 @@ fn check_trait_item<'tcx>(
377378
hir::TraitItemKind::Type(_bounds, Some(ty)) => (None, ty.span),
378379
_ => (None, trait_item.span),
379380
};
381+
380382
check_dyn_incompatible_self_trait_by_name(tcx, trait_item);
383+
check_item_shadowed_by_supertrait(tcx, def_id);
384+
381385
let mut res = check_associated_item(tcx, def_id, span, method_sig);
382386

383387
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
@@ -892,6 +896,45 @@ fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitI
892896
}
893897
}
894898

899+
fn check_item_shadowed_by_supertrait<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_id: LocalDefId) {
900+
let item_name = tcx.item_name(trait_item_def_id.to_def_id());
901+
let trait_def_id = tcx.local_parent(trait_item_def_id);
902+
903+
let shadowed: Vec<_> = traits::supertrait_def_ids(tcx, trait_def_id.to_def_id())
904+
.skip(1)
905+
.flat_map(|supertrait_def_id| {
906+
tcx.associated_items(supertrait_def_id).filter_by_name_unhygienic(item_name)
907+
})
908+
.collect();
909+
if !shadowed.is_empty() {
910+
let shadowee = if let [shadowed] = shadowed[..] {
911+
errors::SupertraitItemShadowee::Labeled {
912+
span: tcx.def_span(shadowed.def_id),
913+
supertrait: tcx.item_name(shadowed.trait_container(tcx).unwrap()),
914+
}
915+
} else {
916+
let (traits, spans): (Vec<_>, Vec<_>) = shadowed
917+
.iter()
918+
.map(|item| {
919+
(tcx.item_name(item.trait_container(tcx).unwrap()), tcx.def_span(item.def_id))
920+
})
921+
.unzip();
922+
errors::SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
923+
};
924+
925+
tcx.emit_node_span_lint(
926+
SUPERTRAIT_ITEM_SHADOWING_DEFINITION,
927+
tcx.local_def_id_to_hir_id(trait_item_def_id),
928+
tcx.def_span(trait_item_def_id),
929+
errors::SupertraitItemShadowing {
930+
item: item_name,
931+
subtrait: tcx.item_name(trait_def_id.to_def_id()),
932+
shadowee,
933+
},
934+
);
935+
}
936+
}
937+
895938
fn check_impl_item<'tcx>(
896939
tcx: TyCtxt<'tcx>,
897940
impl_item: &'tcx hir::ImplItem<'tcx>,

compiler/rustc_hir_analysis/src/errors.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use rustc_errors::codes::*;
44
use rustc_errors::{
5-
Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan,
5+
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
6+
MultiSpan,
67
};
78
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
89
use rustc_middle::ty::Ty;
@@ -1702,3 +1703,28 @@ pub(crate) struct RegisterTypeUnstable<'a> {
17021703
pub span: Span,
17031704
pub ty: Ty<'a>,
17041705
}
1706+
1707+
#[derive(LintDiagnostic)]
1708+
#[diag(hir_analysis_supertrait_item_shadowing)]
1709+
pub(crate) struct SupertraitItemShadowing {
1710+
pub item: Symbol,
1711+
pub subtrait: Symbol,
1712+
#[subdiagnostic]
1713+
pub shadowee: SupertraitItemShadowee,
1714+
}
1715+
1716+
#[derive(Subdiagnostic)]
1717+
pub(crate) enum SupertraitItemShadowee {
1718+
#[note(hir_analysis_supertrait_item_shadowee)]
1719+
Labeled {
1720+
#[primary_span]
1721+
span: Span,
1722+
supertrait: Symbol,
1723+
},
1724+
#[note(hir_analysis_supertrait_item_multiple_shadowee)]
1725+
Several {
1726+
#[primary_span]
1727+
spans: MultiSpan,
1728+
traits: DiagSymbolList,
1729+
},
1730+
}

compiler/rustc_hir_typeck/messages.ftl

+8
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `
192192
193193
hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead
194194
195+
hir_typeck_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
196+
197+
hir_typeck_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
198+
199+
hir_typeck_supertrait_item_shadower = item from `{$subtrait}` shadows a supertrait item
200+
201+
hir_typeck_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
202+
195203
hir_typeck_trivial_cast = trivial {$numeric ->
196204
[true] numeric cast
197205
*[false] cast

compiler/rustc_hir_typeck/src/errors.rs

+35
Original file line numberDiff line numberDiff line change
@@ -866,3 +866,38 @@ pub(crate) struct ReplaceCommaWithSemicolon {
866866
pub comma_span: Span,
867867
pub descr: &'static str,
868868
}
869+
870+
#[derive(LintDiagnostic)]
871+
#[diag(hir_typeck_supertrait_item_shadowing)]
872+
pub(crate) struct SupertraitItemShadowing {
873+
pub item: Symbol,
874+
pub subtrait: Symbol,
875+
#[subdiagnostic]
876+
pub shadower: SupertraitItemShadower,
877+
#[subdiagnostic]
878+
pub shadowee: SupertraitItemShadowee,
879+
}
880+
881+
#[derive(Subdiagnostic)]
882+
#[note(hir_typeck_supertrait_item_shadower)]
883+
pub(crate) struct SupertraitItemShadower {
884+
pub subtrait: Symbol,
885+
#[primary_span]
886+
pub span: Span,
887+
}
888+
889+
#[derive(Subdiagnostic)]
890+
pub(crate) enum SupertraitItemShadowee {
891+
#[note(hir_typeck_supertrait_item_shadowee)]
892+
Labeled {
893+
#[primary_span]
894+
span: Span,
895+
supertrait: Symbol,
896+
},
897+
#[note(hir_typeck_supertrait_item_multiple_shadowee)]
898+
Several {
899+
#[primary_span]
900+
spans: MultiSpan,
901+
traits: DiagSymbolList,
902+
},
903+
}

compiler/rustc_hir_typeck/src/method/confirm.rs

+43
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir_analysis::hir_ty_lowering::{
1010
FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason,
1111
};
1212
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk};
13+
use rustc_lint::builtin::SUPERTRAIT_ITEM_SHADOWING_USAGE;
1314
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
1415
use rustc_middle::ty::adjustment::{
1516
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
@@ -24,6 +25,7 @@ use rustc_trait_selection::traits;
2425
use tracing::debug;
2526

2627
use super::{MethodCallee, probe};
28+
use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing};
2729
use crate::{FnCtxt, callee};
2830

2931
struct ConfirmContext<'a, 'tcx> {
@@ -143,6 +145,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
143145
// Make sure nobody calls `drop()` explicitly.
144146
self.enforce_illegal_method_limitations(pick);
145147

148+
self.enforce_shadowed_supertrait_items(pick, segment);
149+
146150
// Add any trait/regions obligations specified on the method's type parameters.
147151
// We won't add these if we encountered an illegal sized bound, so that we can use
148152
// a custom error in that case.
@@ -665,6 +669,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
665669
}
666670
}
667671

672+
fn enforce_shadowed_supertrait_items(
673+
&self,
674+
pick: &probe::Pick<'_>,
675+
segment: &hir::PathSegment<'tcx>,
676+
) {
677+
if pick.shadowed_candidates.is_empty() {
678+
return;
679+
}
680+
681+
let shadower_span = self.tcx.def_span(pick.item.def_id);
682+
let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap());
683+
let shadower = SupertraitItemShadower { span: shadower_span, subtrait };
684+
685+
let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] {
686+
let shadowee_span = self.tcx.def_span(shadowee.def_id);
687+
let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap());
688+
SupertraitItemShadowee::Labeled { span: shadowee_span, supertrait }
689+
} else {
690+
let (traits, spans): (Vec<_>, Vec<_>) = pick
691+
.shadowed_candidates
692+
.iter()
693+
.map(|item| {
694+
(
695+
self.tcx.item_name(item.trait_container(self.tcx).unwrap()),
696+
self.tcx.def_span(item.def_id),
697+
)
698+
})
699+
.unzip();
700+
SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
701+
};
702+
703+
self.tcx.emit_node_span_lint(
704+
SUPERTRAIT_ITEM_SHADOWING_USAGE,
705+
segment.hir_id,
706+
segment.ident.span,
707+
SupertraitItemShadowing { shadower, shadowee, item: segment.ident.name, subtrait },
708+
);
709+
}
710+
668711
fn upcast(
669712
&mut self,
670713
source_trait_ref: ty::PolyTraitRef<'tcx>,

compiler/rustc_hir_typeck/src/method/probe.rs

+95
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::cmp::max;
33
use std::ops::Deref;
44

55
use rustc_data_structures::fx::FxHashSet;
6+
use rustc_data_structures::sso::SsoHashSet;
67
use rustc_errors::Applicability;
78
use rustc_hir as hir;
89
use rustc_hir::HirId;
@@ -33,6 +34,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{
3334
CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult,
3435
};
3536
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt};
37+
use rustc_type_ir::elaborate::supertrait_def_ids;
3638
use smallvec::{SmallVec, smallvec};
3739
use tracing::{debug, instrument};
3840

@@ -224,6 +226,9 @@ pub(crate) struct Pick<'tcx> {
224226
/// to identify this method. Used only for deshadowing errors.
225227
/// Only applies for inherent impls.
226228
pub receiver_steps: Option<usize>,
229+
230+
/// Candidates that were shadowed by supertraits.
231+
pub shadowed_candidates: Vec<ty::AssocItem>,
227232
}
228233

229234
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1634,6 +1639,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
16341639
}
16351640

16361641
if applicable_candidates.len() > 1 {
1642+
// We collapse to a subtrait pick *after* filtering unstable candidates
1643+
// to make sure we don't prefer a unstable subtrait method over a stable
1644+
// supertrait method.
1645+
if self.tcx.features().supertrait_item_shadowing() {
1646+
if let Some(pick) =
1647+
self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
1648+
{
1649+
return Some(Ok(pick));
1650+
}
1651+
}
1652+
16371653
let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
16381654
return Some(Err(MethodError::Ambiguity(sources)));
16391655
}
@@ -1672,6 +1688,7 @@ impl<'tcx> Pick<'tcx> {
16721688
self_ty,
16731689
unstable_candidates: _,
16741690
receiver_steps: _,
1691+
shadowed_candidates: _,
16751692
} = *self;
16761693
self_ty != other.self_ty || def_id != other.item.def_id
16771694
}
@@ -2081,6 +2098,83 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
20812098
self_ty,
20822099
unstable_candidates: vec![],
20832100
receiver_steps: None,
2101+
shadowed_candidates: vec![],
2102+
})
2103+
}
2104+
2105+
/// Much like `collapse_candidates_to_trait_pick`, this method allows us to collapse
2106+
/// multiple conflicting picks if there is one pick whose trait container is a subtrait
2107+
/// of the trait containers of all of the other picks.
2108+
///
2109+
/// This implements RFC #3624.
2110+
fn collapse_candidates_to_subtrait_pick(
2111+
&self,
2112+
self_ty: Ty<'tcx>,
2113+
probes: &[(&Candidate<'tcx>, ProbeResult)],
2114+
) -> Option<Pick<'tcx>> {
2115+
let mut child_candidate = probes[0].0;
2116+
let mut child_trait = child_candidate.item.trait_container(self.tcx)?;
2117+
let mut supertraits: SsoHashSet<_> = supertrait_def_ids(self.tcx, child_trait).collect();
2118+
2119+
let mut remaining_candidates: Vec<_> = probes[1..].iter().map(|&(p, _)| p).collect();
2120+
while !remaining_candidates.is_empty() {
2121+
let mut made_progress = false;
2122+
let mut next_round = vec![];
2123+
2124+
for remaining_candidate in remaining_candidates {
2125+
let remaining_trait = remaining_candidate.item.trait_container(self.tcx)?;
2126+
if supertraits.contains(&remaining_trait) {
2127+
made_progress = true;
2128+
continue;
2129+
}
2130+
2131+
// This pick is not a supertrait of the `child_pick`.
2132+
// Check if it's a subtrait of the `child_pick`, instead.
2133+
// If it is, then it must have been a subtrait of every
2134+
// other pick we've eliminated at this point. It will
2135+
// take over at this point.
2136+
let remaining_trait_supertraits: SsoHashSet<_> =
2137+
supertrait_def_ids(self.tcx, remaining_trait).collect();
2138+
if remaining_trait_supertraits.contains(&child_trait) {
2139+
child_candidate = remaining_candidate;
2140+
child_trait = remaining_trait;
2141+
supertraits = remaining_trait_supertraits;
2142+
made_progress = true;
2143+
continue;
2144+
}
2145+
2146+
// `child_pick` is not a supertrait of this pick.
2147+
// Don't bail here, since we may be comparing two supertraits
2148+
// of a common subtrait. These two supertraits won't be related
2149+
// at all, but we will pick them up next round when we find their
2150+
// child as we continue iterating in this round.
2151+
next_round.push(remaining_candidate);
2152+
}
2153+
2154+
if made_progress {
2155+
// If we've made progress, iterate again.
2156+
remaining_candidates = next_round;
2157+
} else {
2158+
// Otherwise, we must have at least two candidates which
2159+
// are not related to each other at all.
2160+
return None;
2161+
}
2162+
}
2163+
2164+
Some(Pick {
2165+
item: child_candidate.item,
2166+
kind: TraitPick,
2167+
import_ids: child_candidate.import_ids.clone(),
2168+
autoderefs: 0,
2169+
autoref_or_ptr_adjustment: None,
2170+
self_ty,
2171+
unstable_candidates: vec![],
2172+
shadowed_candidates: probes
2173+
.iter()
2174+
.map(|(c, _)| c.item)
2175+
.filter(|item| item.def_id != child_candidate.item.def_id)
2176+
.collect(),
2177+
receiver_steps: None,
20842178
})
20852179
}
20862180

@@ -2378,6 +2472,7 @@ impl<'tcx> Candidate<'tcx> {
23782472
InherentImplCandidate { receiver_steps, .. } => Some(receiver_steps),
23792473
_ => None,
23802474
},
2475+
shadowed_candidates: vec![],
23812476
}
23822477
}
23832478
}

0 commit comments

Comments
 (0)