From cdfc52fbd638ee3b7b1debad0aef84e730543e8a Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 4 Feb 2021 10:45:17 +0100 Subject: [PATCH 1/4] Try fast_reject::simplify_type in coherence before doing full check Co-authored-by: Jonas Schievink Co-authored-by: Ryan Levick --- .../src/traits/coherence.rs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 99b96f6096476..87f2ea16f8ae3 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -11,7 +11,7 @@ use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionCont use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, fast_reject, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::iter; @@ -67,6 +67,36 @@ where impl2_def_id={:?})", impl1_def_id, impl2_def_id, ); + // Before doing expensive operations like entering an inference context, do + // a quick check via fast_reject to tell if the impl headers could possibly + // unify. + let impl1_self = tcx.type_of(impl1_def_id); + let impl2_self = tcx.type_of(impl2_def_id); + let impl1_ref = tcx.impl_trait_ref(impl1_def_id); + let impl2_ref = tcx.impl_trait_ref(impl2_def_id); + + // Check if any of the input types definitely mismatch. + if impl1_ref + .iter() + .flat_map(|tref| tref.substs.types()) + .zip(impl2_ref.iter().flat_map(|tref| tref.substs.types())) + .chain(iter::once((impl1_self, impl2_self))) + .any(|(ty1, ty2)| { + let ty1 = fast_reject::simplify_type(tcx, ty1, false); + let ty2 = fast_reject::simplify_type(tcx, ty2, false); + if let (Some(ty1), Some(ty2)) = (ty1, ty2) { + // Simplified successfully + ty1 != ty2 + } else { + // Types might unify + false + } + }) + { + // Some types involved are definitely different, so the impls couldn't possibly overlap. + debug!("overlapping_impls: fast_reject early-exit"); + return no_overlap(); + } let overlaps = tcx.infer_ctxt().enter(|infcx| { let selcx = &mut SelectionContext::intercrate(&infcx); From 8ea0973725f18ea3d392b7558165c0fecc589eb8 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Feb 2021 14:04:09 +0100 Subject: [PATCH 2/4] Short circuit full corherence check when dealing with types with different reference mutability --- compiler/rustc_middle/src/ty/sty.rs | 9 +++++++++ .../rustc_trait_selection/src/traits/coherence.rs | 11 ++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 6b4f08d9f9335..6b67d509da00d 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1837,6 +1837,15 @@ impl<'tcx> TyS<'tcx> { ) } + /// Get the mutability of the reference or `None` when not a reference + #[inline] + pub fn ref_mutability(&self) -> Option { + match self.kind() { + Ref(_, _, mutability) => Some(*mutability), + _ => None, + } + } + #[inline] pub fn is_unsafe_ptr(&self) -> bool { matches!(self.kind(), RawPtr(_)) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 87f2ea16f8ae3..afe86b2a23180 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -75,18 +75,19 @@ where let impl1_ref = tcx.impl_trait_ref(impl1_def_id); let impl2_ref = tcx.impl_trait_ref(impl2_def_id); - // Check if any of the input types definitely mismatch. + // Check if any of the input types definitely do not unify. if impl1_ref .iter() .flat_map(|tref| tref.substs.types()) .zip(impl2_ref.iter().flat_map(|tref| tref.substs.types())) .chain(iter::once((impl1_self, impl2_self))) .any(|(ty1, ty2)| { - let ty1 = fast_reject::simplify_type(tcx, ty1, false); - let ty2 = fast_reject::simplify_type(tcx, ty2, false); - if let (Some(ty1), Some(ty2)) = (ty1, ty2) { + let t1 = fast_reject::simplify_type(tcx, ty1, false); + let t2 = fast_reject::simplify_type(tcx, ty2, false); + if let (Some(t1), Some(t2)) = (t1, t2) { // Simplified successfully - ty1 != ty2 + // Types cannot unify if they differ in their reference mutability or simplify to different types + ty1.ref_mutability() != ty2.ref_mutability() || t1 != t2 } else { // Types might unify false From bc5f4c4860d0d1ff05deba9ffb36ffdc9e4a3e96 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Feb 2021 17:22:19 +0100 Subject: [PATCH 3/4] Switch boolean checks --- compiler/rustc_trait_selection/src/traits/coherence.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index afe86b2a23180..82ae4c727731b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -87,7 +87,7 @@ where if let (Some(t1), Some(t2)) = (t1, t2) { // Simplified successfully // Types cannot unify if they differ in their reference mutability or simplify to different types - ty1.ref_mutability() != ty2.ref_mutability() || t1 != t2 + t1 != t2 || ty1.ref_mutability() != ty2.ref_mutability() } else { // Types might unify false From 0cc35f54e87d7abab5d0299003396b3b7f44955f Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Feb 2021 17:30:39 +0100 Subject: [PATCH 4/4] Don't check self type twice --- compiler/rustc_trait_selection/src/traits/coherence.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 82ae4c727731b..e8ae1f44a3671 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -70,8 +70,6 @@ where // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. - let impl1_self = tcx.type_of(impl1_def_id); - let impl2_self = tcx.type_of(impl2_def_id); let impl1_ref = tcx.impl_trait_ref(impl1_def_id); let impl2_ref = tcx.impl_trait_ref(impl2_def_id); @@ -80,14 +78,13 @@ where .iter() .flat_map(|tref| tref.substs.types()) .zip(impl2_ref.iter().flat_map(|tref| tref.substs.types())) - .chain(iter::once((impl1_self, impl2_self))) .any(|(ty1, ty2)| { let t1 = fast_reject::simplify_type(tcx, ty1, false); let t2 = fast_reject::simplify_type(tcx, ty2, false); if let (Some(t1), Some(t2)) = (t1, t2) { // Simplified successfully // Types cannot unify if they differ in their reference mutability or simplify to different types - t1 != t2 || ty1.ref_mutability() != ty2.ref_mutability() + t1 != t2 || ty1.ref_mutability() != ty2.ref_mutability() } else { // Types might unify false