Skip to content

Commit

Permalink
Fix infinite obligation loop for Send/Sync
Browse files Browse the repository at this point in the history
  • Loading branch information
HMPerson1 committed Oct 30, 2018
1 parent ca7408b commit 0fcd88d
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 10 deletions.
37 changes: 29 additions & 8 deletions clippy_lints/src/any_coerce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::rustc::infer::InferCtxt;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::traits;
use crate::rustc::ty::adjustment::Adjust;
use crate::rustc::ty::{self, ToPolyTraitRef, Ty};
use crate::rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax_pos::symbol::Ident;
use crate::utils::{match_def_path, paths, span_lint_and_then};
Expand Down Expand Up @@ -96,21 +96,27 @@ fn check_unsize_coercion<'tcx>(
// redo the typechecking for this coercion to see if it required unsizing something to `dyn Any`
// see https://github.com/rust-lang/rust/blob/cae6efc37d70ab7d353e6ab9ce229d59a65ed643/src/librustc_typeck/check/coercion.rs#L454-L611
let tcx = infcx.tcx;
let coerce_unsized_trait_did = tcx.lang_items().coerce_unsized_trait().unwrap();
let unsize_trait_did = tcx.lang_items().unsize_trait().unwrap();

// don't report overflow errors
let mut selcx = traits::SelectionContext::with_query_mode(&infcx, traits::TraitQueryMode::Canonical);
let mut queue = VecDeque::new();
queue.push_back(
ty::TraitRef::new(
tcx.lang_items().coerce_unsized_trait().unwrap(),
coerce_unsized_trait_did,
tcx.mk_substs_trait(src_ty, &[tgt_ty.into()]),
)
.to_poly_trait_ref(),
);
while let Some(trait_ref) = queue.pop_front() {
if match_def_path(tcx, trait_ref.def_id(), &paths::ANY_TRAIT) {
if_chain! {
if trait_ref.def_id() == unsize_trait_did;
if is_type_dyn_any(tcx, trait_ref.skip_binder().input_types().nth(1).unwrap());
// found something unsizing to `dyn Any`
let coerced_to_any = trait_ref.self_ty();
if type_contains_any(&mut selcx, param_env, coerced_to_any) {
if type_contains_any(&mut selcx, param_env, coerced_to_any);
then {
return Some(LintData { coerced_to_any });
}
}
Expand All @@ -120,12 +126,14 @@ fn check_unsize_coercion<'tcx>(
trait_ref.to_poly_trait_predicate(),
));
if let Ok(Some(vtable)) = select_result {
// we only care about trait predicates
// we only care about trait predicates for these traits
let traits = [coerce_unsized_trait_did, unsize_trait_did];
queue.extend(
vtable
.nested_obligations()
.into_iter()
.filter_map(|oblig| oblig.predicate.to_opt_poly_trait_ref()),
.filter_map(|oblig| oblig.predicate.to_opt_poly_trait_ref())
.filter(|tr| traits.contains(&tr.def_id())),
);
}
}
Expand All @@ -140,8 +148,7 @@ fn type_contains_any<'tcx>(
// check if it derefs to `dyn Any`
if_chain! {
if let Some((any_src_deref_ty, _deref_count)) = fully_deref_type(selcx, param_env, ty);
if let ty::TyKind::Dynamic(trait_list, _) = any_src_deref_ty.sty;
if match_def_path(selcx.tcx(), trait_list.skip_binder().principal().def_id, &paths::ANY_TRAIT);
if is_type_dyn_any(selcx.tcx(), any_src_deref_ty);
then {
// TODO: use deref_count to make a suggestion
return true;
Expand All @@ -151,6 +158,20 @@ fn type_contains_any<'tcx>(
false
}

fn is_type_dyn_any<'tcx>(
tcx: TyCtxt<'_, '_, 'tcx>,
ty: Ty<'tcx>,
) -> bool {
if_chain! {
if let ty::TyKind::Dynamic(trait_list, _) = ty.sty;
if match_def_path(tcx, trait_list.skip_binder().principal().def_id, &paths::ANY_TRAIT);
then {
return true;
}
}
false
}

/// Calls [deref_type] repeatedly
fn fully_deref_type<'tcx>(
selcx: &mut traits::SelectionContext<'_, '_, 'tcx>,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/any_coerce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct Unsizeable<T: ?Sized, U: ?Sized, V: ?Sized> {
}

fn main() {
let mut box_any: Box<dyn Any> = Box::new(Foo);
let mut box_any: Box<dyn Any + Send> = Box::new(Foo);
let _: *mut dyn Any = &mut box_any; // LINT
let _: *mut dyn Any = &mut *box_any; // ok

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/any_coerce.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: coercing `std::boxed::Box<(dyn std::any::Any + 'static)>` to `dyn Any`
error: coercing `std::boxed::Box<(dyn std::any::Any + std::marker::Send + 'static)>` to `dyn Any`
--> $DIR/any_coerce.rs:33:27
|
33 | let _: *mut dyn Any = &mut box_any; // LINT
Expand Down

0 comments on commit 0fcd88d

Please sign in to comment.