Skip to content

Commit

Permalink
Rollup merge of #89461 - crlf0710:dyn_upcasting_lint, r=nikomatsakis
Browse files Browse the repository at this point in the history
Add `deref_into_dyn_supertrait` lint.

Initial implementation of #89460. Resolves #89190.
Maybe also worth a beta backport if necessary.

r? `@nikomatsakis`
  • Loading branch information
GuillaumeGomez authored Oct 7, 2021
2 parents 1584b6a + 250d126 commit ab276b8
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4454,6 +4454,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_lint_defs",
"rustc_macros",
"rustc_middle",
"rustc_parse_format",
Expand Down
46 changes: 46 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3051,6 +3051,7 @@ declare_lint_pass! {
BREAK_WITH_LABEL_AND_LOOP,
UNUSED_ATTRIBUTES,
NON_EXHAUSTIVE_OMITTED_PATTERNS,
DEREF_INTO_DYN_SUPERTRAIT,
]
}

Expand Down Expand Up @@ -3512,3 +3513,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>",
};
}
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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_query_system = { path = "../rustc_query_system" }
rustc_session = { path = "../rustc_session" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()
{
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/traits/trait-upcasting/migrate-lint-deny.rs
Original file line number Diff line number Diff line change
@@ -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() {}
16 changes: 16 additions & 0 deletions src/test/ui/traits/trait-upcasting/migrate-lint-deny.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ab276b8

Please sign in to comment.