Add deref_into_dyn_supertrait lint.

This commit is contained in:
Charles Lew 2021-10-02 19:00:36 +08:00
parent 6e12110812
commit 250d1260e6
6 changed files with 168 additions and 1 deletions

View File

@ -4457,6 +4457,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_lint_defs",
"rustc_macros",
"rustc_middle",
"rustc_parse_format",

View File

@ -3050,6 +3050,7 @@ declare_lint_pass! {
BREAK_WITH_LABEL_AND_LOOP,
UNUSED_ATTRIBUTES,
NON_EXHAUSTIVE_OMITTED_PATTERNS,
DEREF_INTO_DYN_SUPERTRAIT,
]
}
@ -3511,3 +3512,48 @@ declare_lint! {
Allow,
"detect when patterns of types marked `non_exhaustive` are missed",
}
declare_lint! {
/// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the
/// `Deref` implementation with a `dyn SuperTrait` type as `Output`.
///
/// These implementations will become shadowed when the `trait_upcasting` feature is stablized.
/// The `deref` functions will no longer be called implicitly, so there might be behavior change.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(deref_into_dyn_supertrait)]
/// #![allow(dead_code)]
///
/// use core::ops::Deref;
///
/// trait A {}
/// trait B: A {}
/// impl<'a> Deref for dyn 'a + B {
/// type Target = dyn A;
/// fn deref(&self) -> &Self::Target {
/// todo!()
/// }
/// }
///
/// fn take_a(_: &dyn A) { }
///
/// fn take_b(b: &dyn B) {
/// take_a(b);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The dyn upcasting coercion feature adds new coercion rules, taking priority
/// over certain other coercion rules, which will cause some behavior change.
pub DEREF_INTO_DYN_SUPERTRAIT,
Warn,
"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>",
};
}

View File

@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }

View File

@ -6,12 +6,17 @@
//!
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::traits::TraitEngine;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable, WithConstness};
use rustc_target::spec::abi::Abi;
use crate::traits;
use crate::traits::coherence::Conflict;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{util, SelectionResult};
use crate::traits::{Overflow, Unimplemented};
@ -672,6 +677,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
})
}
/// Temporary migration for #89190
fn need_migrate_deref_output_trait_object(
&mut self,
ty: Ty<'tcx>,
cause: &traits::ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<(Ty<'tcx>, DefId)> {
let tcx = self.tcx();
if tcx.features().trait_upcasting {
return None;
}
// <ty as Deref>
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().deref_trait()?,
substs: tcx.mk_substs_trait(ty, &[]),
};
let obligation = traits::Obligation::new(
cause.clone(),
param_env,
ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
);
if !self.infcx.predicate_may_hold(&obligation) {
return None;
}
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
let normalized_ty = fulfillcx.normalize_projection_type(
&self.infcx,
param_env,
ty::ProjectionTy {
item_def_id: tcx.lang_items().deref_target()?,
substs: trait_ref.substs,
},
cause.clone(),
);
let data = if let ty::Dynamic(ref data, ..) = normalized_ty.kind() {
data
} else {
return None;
};
let def_id = data.principal_def_id()?;
return Some((normalized_ty, def_id));
}
/// Searches for unsizing that might apply to `obligation`.
fn assemble_candidates_for_unsizing(
&mut self,
@ -732,6 +786,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let principal_a = data_a.principal().unwrap();
let target_trait_did = principal_def_id_b.unwrap();
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
if let Some((deref_output_ty, deref_output_trait_did)) = self
.need_migrate_deref_output_trait_object(
source,
&obligation.cause,
obligation.param_env,
)
{
if deref_output_trait_did == target_trait_did {
self.tcx().struct_span_lint_hir(
DEREF_INTO_DYN_SUPERTRAIT,
obligation.cause.body_id,
obligation.cause.span,
|lint| {
lint.build(&format!(
"`{}` implements `Deref` with supertrait `{}` as output",
source,
deref_output_ty
)).emit();
},
);
return;
}
}
for (idx, upcast_trait_ref) in
util::supertraits(self.tcx(), source_trait_ref).enumerate()
{

View File

@ -0,0 +1,25 @@
#![deny(deref_into_dyn_supertrait)]
extern crate core;
use core::ops::Deref;
// issue 89190
trait A {}
trait B: A {}
impl<'a> Deref for dyn 'a + B {
type Target = dyn A;
fn deref(&self) -> &Self::Target {
todo!()
}
}
fn take_a(_: &dyn A) {}
fn whoops(b: &dyn B) {
take_a(b)
//~^ ERROR `dyn B` implements `Deref` with supertrait `(dyn A + 'static)` as output
//~^^ WARN this was previously accepted by the compiler but is being phased out;
}
fn main() {}

View File

@ -0,0 +1,16 @@
error: `dyn B` implements `Deref` with supertrait `(dyn A + 'static)` as output
--> $DIR/migrate-lint-deny.rs:20:12
|
LL | take_a(b)
| ^
|
note: the lint level is defined here
--> $DIR/migrate-lint-deny.rs:1:9
|
LL | #![deny(deref_into_dyn_supertrait)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #89460 <https://github.com/rust-lang/rust/issues/89460>
error: aborting due to previous error