Skip to content

Commit 6b99ab4

Browse files
committed
Auto merge of #128912 - compiler-errors:do-not-recommend-impl, r=<try>
Store `do_not_recommend`-ness in impl header Alternative to #128674 It's less flexible, but also less invasive. Hopefully it's also performant. I'd recommend we think separately about the design for how to gate arbitrary diagnostic attributes moving forward.
2 parents 68d2e8a + 4653a9d commit 6b99ab4

File tree

8 files changed

+85
-25
lines changed

8 files changed

+85
-25
lines changed

Diff for: compiler/rustc_hir_analysis/src/coherence/builtin.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_infer::traits::Obligation;
1515
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
1616
use rustc_middle::ty::print::PrintTraitRefExt as _;
1717
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt};
18+
use rustc_span::symbol::{kw, Ident};
1819
use rustc_span::{Span, DUMMY_SP};
1920
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2021
use rustc_trait_selection::traits::misc::{
@@ -103,26 +104,50 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
103104

104105
let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
105106
match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
106-
Ok(()) => Ok(()),
107+
Ok(()) => {}
107108
Err(CopyImplementationError::InfringingFields(fields)) => {
108109
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
109-
Err(infringing_fields_error(
110+
return Err(infringing_fields_error(
110111
tcx,
111112
fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),
112113
LangItem::Copy,
113114
impl_did,
114115
span,
115-
))
116+
));
116117
}
117118
Err(CopyImplementationError::NotAnAdt) => {
118119
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
119-
Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }))
120+
return Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }));
120121
}
121122
Err(CopyImplementationError::HasDestructor) => {
122123
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
123-
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
124+
return Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }));
125+
}
126+
}
127+
128+
// Let's additionally lint the fields of the copy impl.
129+
if let ty::Adt(def, _) = *self_type.kind() {
130+
let impl_span = tcx.def_span(impl_did);
131+
let (_, scope) = tcx.adjust_ident_and_get_scope(
132+
Ident::new(kw::Empty, impl_span),
133+
def.did(),
134+
tcx.local_def_id_to_hir_id(impl_did),
135+
);
136+
if tcx.crate_name(scope.krate).as_str() == "core" {
137+
return Ok(());
138+
}
139+
println!("{scope:?}");
140+
let infringing_fields: Vec<_> =
141+
def.all_fields().filter(|field| !field.vis.is_accessible_from(scope, tcx)).collect();
142+
if !infringing_fields.is_empty() {
143+
rustc_middle::span_bug!(
144+
impl_span,
145+
"{infringing_fields:#?} in {scope:#?} for {impl_did:#?}"
146+
);
124147
}
125148
}
149+
150+
Ok(())
126151
}
127152

128153
fn visit_implementation_of_const_param_ty(

Diff for: compiler/rustc_hir_analysis/src/collect.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,8 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
16991699
trait_ref: ty::EarlyBinder::bind(trait_ref),
17001700
safety: impl_.safety,
17011701
polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
1702+
do_not_recommend: tcx.features().do_not_recommend
1703+
&& tcx.has_attrs_with_path(def_id, &[sym::diagnostic, sym::do_not_recommend]),
17021704
}
17031705
})
17041706
}

Diff for: compiler/rustc_middle/src/ty/context.rs

+6
Original file line numberDiff line numberDiff line change
@@ -3176,6 +3176,12 @@ impl<'tcx> TyCtxt<'tcx> {
31763176
pub fn impl_polarity(self, def_id: impl IntoQueryParam<DefId>) -> ty::ImplPolarity {
31773177
self.impl_trait_header(def_id).map_or(ty::ImplPolarity::Positive, |h| h.polarity)
31783178
}
3179+
3180+
/// Whether this is a trait implementation that has `#[diagnostic::do_not_recommend]`
3181+
pub fn do_not_recommend_impl(self, def_id: DefId) -> bool {
3182+
matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true })
3183+
&& self.impl_trait_header(def_id).is_some_and(|header| header.do_not_recommend)
3184+
}
31793185
}
31803186

31813187
/// Parameter attributes that can only be determined by examining the body of a function instead

Diff for: compiler/rustc_middle/src/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ pub struct ImplTraitHeader<'tcx> {
262262
pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>,
263263
pub polarity: ImplPolarity,
264264
pub safety: hir::Safety,
265+
pub do_not_recommend: bool,
265266
}
266267

267268
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]

Diff for: compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+3-15
Original file line numberDiff line numberDiff line change
@@ -687,10 +687,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
687687
let mut applied_do_not_recommend = false;
688688
loop {
689689
if let ObligationCauseCode::ImplDerived(ref c) = base_cause {
690-
if self.tcx.has_attrs_with_path(
691-
c.impl_or_alias_def_id,
692-
&[sym::diagnostic, sym::do_not_recommend],
693-
) {
690+
if self.tcx.do_not_recommend_impl(c.impl_or_alias_def_id) {
694691
let code = (*c.derived.parent_code).clone();
695692
obligation.cause.map_code(|_| code);
696693
obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx);
@@ -1630,11 +1627,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16301627
.tcx
16311628
.all_impls(def_id)
16321629
// ignore `do_not_recommend` items
1633-
.filter(|def_id| {
1634-
!self
1635-
.tcx
1636-
.has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend])
1637-
})
1630+
.filter(|def_id| !self.tcx.do_not_recommend_impl(*def_id))
16381631
// Ignore automatically derived impls and `!Trait` impls.
16391632
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
16401633
.filter_map(|header| {
@@ -1904,12 +1897,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
19041897
let impl_candidates = impl_candidates
19051898
.into_iter()
19061899
.cloned()
1907-
.filter(|cand| {
1908-
!self.tcx.has_attrs_with_path(
1909-
cand.impl_def_id,
1910-
&[sym::diagnostic, sym::do_not_recommend],
1911-
)
1912-
})
1900+
.filter(|cand| !self.tcx.do_not_recommend_impl(cand.impl_def_id))
19131901
.collect::<Vec<_>>();
19141902

19151903
let def_id = trait_ref.def_id();

Diff for: compiler/rustc_trait_selection/src/solve/fulfill.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use rustc_middle::bug;
1313
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1414
use rustc_middle::ty::{self, TyCtxt};
1515
use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
16-
use rustc_span::symbol::sym;
1716

1817
use super::delegate::SolverDelegate;
1918
use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
@@ -440,10 +439,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
440439
source: CandidateSource::Impl(impl_def_id),
441440
result: _,
442441
} = candidate.kind()
443-
&& goal
444-
.infcx()
445-
.tcx
446-
.has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend])
442+
&& goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
447443
{
448444
return ControlFlow::Break(self.obligation.clone());
449445
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![allow(unknown_or_malformed_diagnostic_attributes)]
2+
3+
trait Foo {}
4+
5+
#[diagnostic::do_not_recommend]
6+
impl<A> Foo for (A,) {}
7+
8+
#[diagnostic::do_not_recommend]
9+
impl<A, B> Foo for (A, B) {}
10+
11+
#[diagnostic::do_not_recommend]
12+
impl<A, B, C> Foo for (A, B, C) {}
13+
14+
impl Foo for i32 {}
15+
16+
fn check(a: impl Foo) {}
17+
18+
fn main() {
19+
check(());
20+
//~^ ERROR the trait bound `(): Foo` is not satisfied
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0277]: the trait bound `(): Foo` is not satisfied
2+
--> $DIR/do_not_apply_attribute_without_feature_flag.rs:19:11
3+
|
4+
LL | check(());
5+
| ----- ^^ the trait `Foo` is not implemented for `()`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `Foo`:
10+
(A, B)
11+
(A, B, C)
12+
(A,)
13+
note: required by a bound in `check`
14+
--> $DIR/do_not_apply_attribute_without_feature_flag.rs:16:18
15+
|
16+
LL | fn check(a: impl Foo) {}
17+
| ^^^ required by this bound in `check`
18+
19+
error: aborting due to 1 previous error
20+
21+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)