Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RFC 3624 supertrait_item_shadowing (v2) #125782

Merged
merged 5 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,8 @@ declare_features! (
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
/// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
/// Allows subtrait items to shadow supertrait items.
(unstable, supertrait_item_shadowing, "CURRENT_RUSTC_VERSION", Some(89151)),
/// Allows using `#[thread_local]` on `static` items.
(unstable, thread_local, "1.0.0", Some(29594)),
/// Allows defining `trait X = A + B;` alias items.
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,12 @@ hir_analysis_specialization_trait = implementing `rustc_specialization_trait` tr
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
hir_analysis_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}
hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item
hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{AmbigArg, ItemKind};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_lint_defs::builtin::SUPERTRAIT_ITEM_SHADOWING_DEFINITION;
use rustc_macros::LintDiagnostic;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::query::Providers;
Expand Down Expand Up @@ -388,7 +389,12 @@ fn check_trait_item<'tcx>(
hir::TraitItemKind::Type(_bounds, Some(ty)) => (None, ty.span),
_ => (None, trait_item.span),
};

check_dyn_incompatible_self_trait_by_name(tcx, trait_item);

// Check that an item definition in a subtrait is shadowing a supertrait item.
lint_item_shadowing_supertrait_item(tcx, def_id);

let mut res = check_associated_item(tcx, def_id, span, method_sig);

if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
Expand Down Expand Up @@ -898,6 +904,45 @@ fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitI
}
}

fn lint_item_shadowing_supertrait_item<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_id: LocalDefId) {
let item_name = tcx.item_name(trait_item_def_id.to_def_id());
let trait_def_id = tcx.local_parent(trait_item_def_id);

let shadowed: Vec<_> = traits::supertrait_def_ids(tcx, trait_def_id.to_def_id())
.skip(1)
.flat_map(|supertrait_def_id| {
tcx.associated_items(supertrait_def_id).filter_by_name_unhygienic(item_name)
})
.collect();
if !shadowed.is_empty() {
let shadowee = if let [shadowed] = shadowed[..] {
errors::SupertraitItemShadowee::Labeled {
span: tcx.def_span(shadowed.def_id),
supertrait: tcx.item_name(shadowed.trait_container(tcx).unwrap()),
}
} else {
let (traits, spans): (Vec<_>, Vec<_>) = shadowed
.iter()
.map(|item| {
(tcx.item_name(item.trait_container(tcx).unwrap()), tcx.def_span(item.def_id))
})
.unzip();
errors::SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
};

tcx.emit_node_span_lint(
SUPERTRAIT_ITEM_SHADOWING_DEFINITION,
tcx.local_def_id_to_hir_id(trait_item_def_id),
tcx.def_span(trait_item_def_id),
errors::SupertraitItemShadowing {
item: item_name,
subtrait: tcx.item_name(trait_def_id.to_def_id()),
shadowee,
},
);
}
}

fn check_impl_item<'tcx>(
tcx: TyCtxt<'tcx>,
impl_item: &'tcx hir::ImplItem<'tcx>,
Expand Down
28 changes: 27 additions & 1 deletion compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
use rustc_abi::ExternAbi;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan,
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
MultiSpan,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
Expand Down Expand Up @@ -1733,3 +1734,28 @@ pub(crate) struct RegisterTypeUnstable<'a> {
pub span: Span,
pub ty: Ty<'a>,
}

#[derive(LintDiagnostic)]
#[diag(hir_analysis_supertrait_item_shadowing)]
pub(crate) struct SupertraitItemShadowing {
pub item: Symbol,
pub subtrait: Symbol,
#[subdiagnostic]
pub shadowee: SupertraitItemShadowee,
}

#[derive(Subdiagnostic)]
pub(crate) enum SupertraitItemShadowee {
#[note(hir_analysis_supertrait_item_shadowee)]
Labeled {
#[primary_span]
span: Span,
supertrait: Symbol,
},
#[note(hir_analysis_supertrait_item_multiple_shadowee)]
Several {
#[primary_span]
spans: MultiSpan,
traits: DiagSymbolList,
},
}
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `

hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead

hir_typeck_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits}

hir_typeck_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item

hir_typeck_supertrait_item_shadower = item from `{$subtrait}` shadows a supertrait item

hir_typeck_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait

hir_typeck_trivial_cast = trivial {$numeric ->
[true] numeric cast
*[false] cast
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,3 +868,38 @@ pub(crate) struct ReplaceCommaWithSemicolon {
pub comma_span: Span,
pub descr: &'static str,
}

#[derive(LintDiagnostic)]
#[diag(hir_typeck_supertrait_item_shadowing)]
pub(crate) struct SupertraitItemShadowing {
pub item: Symbol,
pub subtrait: Symbol,
#[subdiagnostic]
pub shadower: SupertraitItemShadower,
#[subdiagnostic]
pub shadowee: SupertraitItemShadowee,
}

#[derive(Subdiagnostic)]
#[note(hir_typeck_supertrait_item_shadower)]
pub(crate) struct SupertraitItemShadower {
pub subtrait: Symbol,
#[primary_span]
pub span: Span,
}

#[derive(Subdiagnostic)]
pub(crate) enum SupertraitItemShadowee {
#[note(hir_typeck_supertrait_item_shadowee)]
Labeled {
#[primary_span]
span: Span,
supertrait: Symbol,
},
#[note(hir_typeck_supertrait_item_multiple_shadowee)]
Several {
#[primary_span]
spans: MultiSpan,
traits: DiagSymbolList,
},
}
48 changes: 46 additions & 2 deletions compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_hir_analysis::hir_ty_lowering::{
FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason,
};
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk};
use rustc_lint::builtin::SUPERTRAIT_ITEM_SHADOWING_USAGE;
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
Expand All @@ -24,6 +25,7 @@ use rustc_trait_selection::traits;
use tracing::debug;

use super::{MethodCallee, probe};
use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing};
use crate::{FnCtxt, callee};

struct ConfirmContext<'a, 'tcx> {
Expand Down Expand Up @@ -141,7 +143,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
let method_sig = ty::Binder::dummy(method_sig);

// Make sure nobody calls `drop()` explicitly.
self.enforce_illegal_method_limitations(pick);
self.check_for_illegal_method_calls(pick);

// Lint when an item is shadowing a supertrait item.
self.lint_shadowed_supertrait_items(pick, segment);

// Add any trait/regions obligations specified on the method's type parameters.
// We won't add these if we encountered an illegal sized bound, so that we can use
Expand Down Expand Up @@ -656,7 +661,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
})
}

fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
fn check_for_illegal_method_calls(&self, pick: &probe::Pick<'_>) {
// Disallow calls to the method `drop` defined in the `Drop` trait.
if let Some(trait_def_id) = pick.item.trait_container(self.tcx) {
if let Err(e) = callee::check_legal_trait_for_method_call(
Expand All @@ -672,6 +677,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
}
}

fn lint_shadowed_supertrait_items(
&self,
pick: &probe::Pick<'_>,
segment: &hir::PathSegment<'tcx>,
) {
if pick.shadowed_candidates.is_empty() {
return;
}

let shadower_span = self.tcx.def_span(pick.item.def_id);
let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap());
let shadower = SupertraitItemShadower { span: shadower_span, subtrait };

let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] {
let shadowee_span = self.tcx.def_span(shadowee.def_id);
let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap());
SupertraitItemShadowee::Labeled { span: shadowee_span, supertrait }
} else {
let (traits, spans): (Vec<_>, Vec<_>) = pick
.shadowed_candidates
.iter()
.map(|item| {
(
self.tcx.item_name(item.trait_container(self.tcx).unwrap()),
self.tcx.def_span(item.def_id),
)
})
.unzip();
SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() }
};

self.tcx.emit_node_span_lint(
SUPERTRAIT_ITEM_SHADOWING_USAGE,
segment.hir_id,
segment.ident.span,
SupertraitItemShadowing { shadower, shadowee, item: segment.ident.name, subtrait },
);
}

fn upcast(
&mut self,
source_trait_ref: ty::PolyTraitRef<'tcx>,
Expand Down
Loading
Loading