Skip to content

Commit

Permalink
Rollup merge of rust-lang#128912 - compiler-errors:do-not-recommend-i…
Browse files Browse the repository at this point in the history
…mpl, r=lcnr

Store `do_not_recommend`-ness in impl header

Alternative to rust-lang#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.
  • Loading branch information
matthiaskrgr committed Aug 12, 2024
2 parents 47fdb09 + 20a16bb commit 3f5cd14
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 20 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,8 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
trait_ref: ty::EarlyBinder::bind(trait_ref),
safety: impl_.safety,
polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
do_not_recommend: tcx.features().do_not_recommend
&& tcx.has_attrs_with_path(def_id, &[sym::diagnostic, sym::do_not_recommend]),
}
})
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3176,6 +3176,12 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn impl_polarity(self, def_id: impl IntoQueryParam<DefId>) -> ty::ImplPolarity {
self.impl_trait_header(def_id).map_or(ty::ImplPolarity::Positive, |h| h.polarity)
}

/// Whether this is a trait implementation that has `#[diagnostic::do_not_recommend]`
pub fn do_not_recommend_impl(self, def_id: DefId) -> bool {
matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true })
&& self.impl_trait_header(def_id).is_some_and(|header| header.do_not_recommend)
}
}

/// Parameter attributes that can only be determined by examining the body of a function instead
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ pub struct ImplTraitHeader<'tcx> {
pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>,
pub polarity: ImplPolarity,
pub safety: hir::Safety,
pub do_not_recommend: bool,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -687,10 +687,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let mut applied_do_not_recommend = false;
loop {
if let ObligationCauseCode::ImplDerived(ref c) = base_cause {
if self.tcx.has_attrs_with_path(
c.impl_or_alias_def_id,
&[sym::diagnostic, sym::do_not_recommend],
) {
if self.tcx.do_not_recommend_impl(c.impl_or_alias_def_id) {
let code = (*c.derived.parent_code).clone();
obligation.cause.map_code(|_| code);
obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx);
Expand Down Expand Up @@ -1629,11 +1626,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.tcx
.all_impls(def_id)
// ignore `do_not_recommend` items
.filter(|def_id| {
!self
.tcx
.has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend])
})
.filter(|def_id| !self.tcx.do_not_recommend_impl(*def_id))
// Ignore automatically derived impls and `!Trait` impls.
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
.filter_map(|header| {
Expand Down Expand Up @@ -1903,12 +1896,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let impl_candidates = impl_candidates
.into_iter()
.cloned()
.filter(|cand| {
!self.tcx.has_attrs_with_path(
cand.impl_def_id,
&[sym::diagnostic, sym::do_not_recommend],
)
})
.filter(|cand| !self.tcx.do_not_recommend_impl(cand.impl_def_id))
.collect::<Vec<_>>();

let def_id = trait_ref.def_id();
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use rustc_middle::bug;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, TyCtxt};
use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
use rustc_span::symbol::sym;

use super::delegate::SolverDelegate;
use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
Expand Down Expand Up @@ -440,10 +439,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
source: CandidateSource::Impl(impl_def_id),
result: _,
} = candidate.kind()
&& goal
.infcx()
.tcx
.has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend])
&& goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
{
return ControlFlow::Break(self.obligation.clone());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![allow(unknown_or_malformed_diagnostic_attributes)]

trait Foo {}

#[diagnostic::do_not_recommend]
impl<A> Foo for (A,) {}

#[diagnostic::do_not_recommend]
impl<A, B> Foo for (A, B) {}

#[diagnostic::do_not_recommend]
impl<A, B, C> Foo for (A, B, C) {}

impl Foo for i32 {}

fn check(a: impl Foo) {}

fn main() {
check(());
//~^ ERROR the trait bound `(): Foo` is not satisfied
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0277]: the trait bound `(): Foo` is not satisfied
--> $DIR/do_not_apply_attribute_without_feature_flag.rs:19:11
|
LL | check(());
| ----- ^^ the trait `Foo` is not implemented for `()`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Foo`:
(A, B)
(A, B, C)
(A,)
note: required by a bound in `check`
--> $DIR/do_not_apply_attribute_without_feature_flag.rs:16:18
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 3f5cd14

Please sign in to comment.