From a5c707aadc0da2e3a12af48cc1bed20884827702 Mon Sep 17 00:00:00 2001 From: Gianni Ciccarelli Date: Tue, 3 Apr 2018 07:26:22 +0000 Subject: [PATCH 1/2] intersection impls --- src/librustc/traits/coherence.rs | 14 +- src/librustc/traits/specialize/mod.rs | 39 +- .../traits/specialize/specialization_graph.rs | 355 ++++++++++++------ ...herence-conflicting-negative-trait-impl.rs | 1 + .../specialization/intersection-impl.rs | 36 ++ .../specialization/intersection-impl-2.rs | 63 ++++ .../specialization/intersection-impl.rs | 42 +++ 7 files changed, 423 insertions(+), 127 deletions(-) create mode 100644 src/test/compile-fail/specialization/intersection-impl.rs create mode 100644 src/test/run-pass/specialization/intersection-impl-2.rs create mode 100644 src/test/run-pass/specialization/intersection-impl.rs diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index 31f8af1f96872..1a763587f0eb7 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -8,15 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! See rustc guide chapters on [trait-resolution] and [trait-specialization] for more info on how -//! this works. -//! -//! [trait-resolution]: https://rust-lang-nursery.github.io/rustc-guide/trait-resolution.html -//! [trait-specialization]: https://rust-lang-nursery.github.io/rustc-guide/trait-specialization.html +//! See `README.md` for high-level documentation use hir::def_id::{DefId, LOCAL_CRATE}; use syntax_pos::DUMMY_SP; -use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause}; +use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal}; use traits::IntercrateMode; use traits::select::IntercrateAmbiguityCause; use ty::{self, Ty, TyCtxt}; @@ -123,7 +119,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, // types into scope; instead, we replace the generic types with // fresh type variables, and hence we do our evaluations in an // empty environment. - let param_env = ty::ParamEnv::empty(); + let param_env = ty::ParamEnv::empty(Reveal::UserFacing); let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id); let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id); @@ -140,7 +136,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, Err(_) => return None }; - debug!("overlap: unification check succeeded"); + debug!("overlap: unification check succeeded {:?}", obligations); // Are any of the obligations unsatisfiable? If so, no overlap. let infcx = selcx.infcx(); @@ -281,7 +277,7 @@ pub fn orphan_check<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, /// is bad, because the only local type with `T` as a subtree is /// `LocalType`, and `Vec<->` is between it and the type parameter. /// - similarly, `FundamentalPair, T>` is bad, because -/// the second occurrence of `T` is not a subtree of *any* local type. +/// the second occurence of `T` is not a subtree of *any* local type. /// - however, `LocalType>` is OK, because `T` is a subtree of /// `LocalType>`, which is local and has no types between it and /// the type parameter. diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 30b2c55afa194..11e7e4f6b9a1a 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -42,6 +42,7 @@ pub struct OverlapError { pub trait_desc: String, pub self_desc: Option, pub intercrate_ambiguity_causes: Vec, + pub used_to_be_allowed: bool, } /// Given a subst for the requested impl, translate it to a subst @@ -324,29 +325,48 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx def_id.index.as_array_index()) }); + let mut overlaps = Vec::new(); + for impl_def_id in trait_impls { if impl_def_id.is_local() { - // This is where impl overlap checking happens: let insert_result = sg.insert(tcx, impl_def_id); - // Report error if there was one. - let (overlap, used_to_be_allowed) = match insert_result { - Err(overlap) => (Some(overlap), false), - Ok(opt_overlap) => (opt_overlap, true) + match insert_result { + Ok(Some(mut opt_overlaps)) => { + // record any overlap that occurs between two impl + // later those recordings are processed to establish + // if an intersection impl is present between two overlapping impls + // if no an overlap error is emitted + for opt_overlap in opt_overlaps { + overlaps.push((impl_def_id, opt_overlap)); + } + }, + _ => {}, }; - if let Some(overlap) = overlap { + } else { + let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); + sg.record_impl_from_cstore(tcx, parent, impl_def_id) + } + } + + if overlaps.len() > 0 { + // Build the graph only if there is at least an overlap + let (graph, nodes_idx) = sg.build_graph(); + for (impl_def_id, overlap) in overlaps { + if !sg.check_overlap(&graph, &nodes_idx, impl_def_id, overlap.with_impl) { + let msg = format!("conflicting implementations of trait `{}`{}:{}", overlap.trait_desc, overlap.self_desc.clone().map_or( String::new(), |ty| { format!(" for type `{}`", ty) }), - if used_to_be_allowed { " (E0119)" } else { "" } + if overlap.used_to_be_allowed { " (E0119)" } else { "" } ); let impl_span = tcx.sess.codemap().def_span( tcx.span_of_impl(impl_def_id).unwrap() ); - let mut err = if used_to_be_allowed { + let mut err = if overlap.used_to_be_allowed { tcx.struct_span_lint_node( lint::builtin::INCOHERENT_FUNDAMENTAL_IMPLS, tcx.hir.as_local_node_id(impl_def_id).unwrap(), @@ -386,9 +406,6 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx err.emit(); } - } else { - let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); - sg.record_impl_from_cstore(tcx, parent, impl_def_id) } } diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index e56a8662f3eb4..590460b06c2a4 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -12,8 +12,8 @@ use super::OverlapError; use hir::def_id::DefId; use ich::{self, StableHashingContext}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, - StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use rustc_data_structures::graph::{Graph as DataStructuresGraph, NodeIndex, INCOMING}; use traits; use ty::{self, TyCtxt, TypeFoldable}; use ty::fast_reject::{self, SimplifiedType}; @@ -39,9 +39,9 @@ use util::nodemap::{DefIdMap, FxHashMap}; /// has at most one parent. #[derive(RustcEncodable, RustcDecodable)] pub struct Graph { - // all impls have a parent; the "root" impls have as their parent the def_id + // all impls have one or more parents; the "root" impls have as their parent the def_id // of the trait - parent: DefIdMap, + parent: DefIdMap>, // the "root" impls are found by looking up the trait's def_id. children: DefIdMap, @@ -60,7 +60,6 @@ struct Children { // A similar division is used within `TraitDef`, but the lists there collect // together *all* the impls for a trait, and are populated prior to building // the specialization graph. - /// Impls of the trait. nonblanket_impls: FxHashMap>, @@ -89,12 +88,13 @@ impl<'a, 'gcx, 'tcx> Children { } /// Insert an impl into this set of children without comparing to any existing impls - fn insert_blindly(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - impl_def_id: DefId) { + fn insert_blindly(&mut self, tcx: TyCtxt<'a, 'gcx, 'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - self.nonblanket_impls.entry(sty).or_insert(vec![]).push(impl_def_id) + self.nonblanket_impls + .entry(sty) + .or_insert(vec![]) + .push(impl_def_id) } else { self.blanket_impls.push(impl_def_id) } @@ -102,21 +102,26 @@ impl<'a, 'gcx, 'tcx> Children { /// Attempt to insert an impl into this set of children, while comparing for /// specialization relationships. - fn insert(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - impl_def_id: DefId, - simplified_self: Option) - -> Result - { + fn insert( + &mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + impl_def_id: DefId, + simplified_self: Option, + ) -> Result, OverlapError> { let mut last_lint = None; + // Nodes could specialize more than one parent + // so every impl must visited in order to properly place it + let mut inserted_results = Vec::new(); + for slot in match simplified_self { Some(sty) => self.filtered_mut(sty), None => self.iter_mut(), } { let possible_sibling = *slot; - let overlap_error = |overlap: traits::coherence::OverlapResult| { + let overlap_error = |overlap: traits::coherence::OverlapResult, + used_to_be_allowed: bool| { // overlap, but no specialization; error out let trait_ref = overlap.impl_header.trait_ref.unwrap(); let self_ty = trait_ref.self_ty(); @@ -132,45 +137,52 @@ impl<'a, 'gcx, 'tcx> Children { None }, intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, + used_to_be_allowed: used_to_be_allowed, } }; let tcx = tcx.global_tcx(); - let (le, ge) = traits::overlapping_impls( + let (le, ge, overlap_er) = traits::overlapping_impls( tcx, possible_sibling, impl_def_id, traits::IntercrateMode::Issue43355, |overlap| { if tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) { - return Ok((false, false)); + return Ok((false, false, None)); } let le = tcx.specializes((impl_def_id, possible_sibling)); let ge = tcx.specializes((possible_sibling, impl_def_id)); if le == ge { - Err(overlap_error(overlap)) + Ok((true, true, Some(overlap_error(overlap, false)))) } else { - Ok((le, ge)) + Ok((le, ge, None)) } }, - || Ok((false, false)), + || Ok((false, false, None)), )?; if le && !ge { - debug!("descending as child of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap()); + debug!( + "descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap() + ); // the impl specializes possible_sibling - return Ok(Inserted::ShouldRecurseOn(possible_sibling)); + inserted_results.push(Inserted::ShouldRecurseOn(possible_sibling)); } else if ge && !le { - debug!("placing as parent of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap()); - - // possible_sibling specializes the impl - *slot = impl_def_id; - return Ok(Inserted::Replaced(possible_sibling)); + debug!( + "placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap() + ); + + // possible_sibling specializes the impl + *slot = impl_def_id; + inserted_results.push(Inserted::Replaced(possible_sibling)); + } else if ge && le { + last_lint = overlap_er; } else { if !tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) { traits::overlapping_impls( @@ -178,7 +190,7 @@ impl<'a, 'gcx, 'tcx> Children { possible_sibling, impl_def_id, traits::IntercrateMode::Fixed, - |overlap| last_lint = Some(overlap_error(overlap)), + |overlap| last_lint = Some(overlap_error(overlap, true)), || (), ); } @@ -187,20 +199,28 @@ impl<'a, 'gcx, 'tcx> Children { } } + if inserted_results.len() > 0 { + return Ok(inserted_results); + } + // no overlap with any potential siblings, so add as a new sibling debug!("placing as new sibling"); self.insert_blindly(tcx, impl_def_id); - Ok(Inserted::BecameNewSibling(last_lint)) + Ok(vec![Inserted::BecameNewSibling(last_lint)]) } - fn iter_mut(&'a mut self) -> Box + 'a> { - let nonblanket = self.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter_mut()); + fn iter_mut(&'a mut self) -> Box + 'a> { + let nonblanket = self.nonblanket_impls + .iter_mut() + .flat_map(|(_, v)| v.iter_mut()); Box::new(self.blanket_impls.iter_mut().chain(nonblanket)) } - fn filtered_mut(&'a mut self, sty: SimplifiedType) - -> Box + 'a> { - let nonblanket = self.nonblanket_impls.entry(sty).or_insert(vec![]).iter_mut(); + fn filtered_mut(&'a mut self, sty: SimplifiedType) -> Box + 'a> { + let nonblanket = self.nonblanket_impls + .entry(sty) + .or_insert(vec![]) + .iter_mut(); Box::new(self.blanket_impls.iter_mut().chain(nonblanket)) } } @@ -216,83 +236,178 @@ impl<'a, 'gcx, 'tcx> Graph { /// Insert a local impl into the specialization graph. If an existing impl /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. - pub fn insert(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - impl_def_id: DefId) - -> Result, OverlapError> { + pub fn insert( + &mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + impl_def_id: DefId, + ) -> Result>, OverlapError> { assert!(impl_def_id.is_local()); let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; - debug!("insert({:?}): inserting TraitRef {:?} into specialization graph", - impl_def_id, trait_ref); + debug!( + "insert({:?}): inserting TraitRef {:?} into specialization graph", + impl_def_id, trait_ref + ); // if the reference itself contains an earlier error (e.g., due to a // resolution failure), then we just insert the impl at the top level of // the graph and claim that there's no overlap (in order to suppress // bogus errors). if trait_ref.references_error() { - debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \ - impl_def_id={:?}, trait_def_id={:?}", - trait_ref, impl_def_id, trait_def_id); - - self.parent.insert(impl_def_id, trait_def_id); - self.children.entry(trait_def_id).or_insert(Children::new()) + debug!( + "insert: inserting dummy node for erroneous TraitRef {:?}, \ + impl_def_id={:?}, trait_def_id={:?}", + trait_ref, impl_def_id, trait_def_id + ); + + self.parent_insert(impl_def_id, trait_def_id); + self.children + .entry(trait_def_id) + .or_insert(Children::new()) .insert_blindly(tcx, impl_def_id); return Ok(None); } - let mut parent = trait_def_id; - let mut last_lint = None; + let parent = trait_def_id; let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false); - // Descend the specialization tree, where `parent` is the current parent node - loop { - use self::Inserted::*; - - let insert_result = self.children.entry(parent).or_insert(Children::new()) - .insert(tcx, impl_def_id, simplified)?; + let mut last_lints = vec![]; + // Recusively descend the specialization tree, where `parent` is the current parent node + self.recursive_insert(parent, tcx, impl_def_id, simplified, &mut last_lints); + Ok(Some(last_lints)) + } - match insert_result { - BecameNewSibling(opt_lint) => { - last_lint = opt_lint; - break; - } - Replaced(new_child) => { - self.parent.insert(new_child, impl_def_id); - let mut new_children = Children::new(); - new_children.insert_blindly(tcx, new_child); - self.children.insert(impl_def_id, new_children); - break; - } - ShouldRecurseOn(new_parent) => { - parent = new_parent; + fn recursive_insert( + &mut self, + parent: DefId, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + impl_def_id: DefId, + simplified: Option, + last_lints: &mut Vec, + ) { + use self::Inserted::*; + match self.children + .entry(parent) + .or_insert(Children::new()) + .insert(tcx, impl_def_id, simplified) + { + Ok(insert_results) => for insert_result in insert_results { + match insert_result { + BecameNewSibling(opt_lint) => { + if opt_lint.is_some() { + last_lints.push(opt_lint.unwrap()); + } + self.parent_insert(impl_def_id, parent); + } + Replaced(new_child) => { + self.parent_insert(new_child, impl_def_id); + let mut new_children = Children::new(); + new_children.insert_blindly(tcx, new_child); + self.children.insert(impl_def_id, new_children); + self.parent_insert(impl_def_id, parent); + } + ShouldRecurseOn(new_parent) => { + self.recursive_insert(new_parent, tcx, impl_def_id, simplified, last_lints); + } } - } + }, + _ => {} } + } - self.parent.insert(impl_def_id, parent); - Ok(last_lint) + fn parent_insert(&mut self, key: DefId, value: DefId) -> Option { + if self.parent.contains_key(&key) { + let mut impl_vec = self.parent.get(&key).unwrap().clone(); + impl_vec.push(value); + self.parent.insert(key, impl_vec); + Some(value) + } else { + if self.parent.insert(key, vec![value]).is_some() { + Some(value) + } else { + None + } + } } /// Insert cached metadata mapping from a child impl back to its parent. - pub fn record_impl_from_cstore(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - parent: DefId, - child: DefId) { - if self.parent.insert(child, parent).is_some() { - bug!("When recording an impl from the crate store, information about its parent \ - was already present."); + pub fn record_impl_from_cstore( + &mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + parent: DefId, + child: DefId, + ) { + if self.parent_insert(child, parent).is_some() { + bug!( + "When recording an impl from the crate store, information about its parent \ + was already present." + ); } - self.children.entry(parent).or_insert(Children::new()).insert_blindly(tcx, child); + self.children + .entry(parent) + .or_insert(Children::new()) + .insert_blindly(tcx, child); + } + + /// Returns the def-id of the parent impl(s) for a given impl. + /// An impl A has a parent impl B if A matches a strict subset of the types that B matches. + pub fn parents(&self, child: DefId) -> Vec { + self.parent.get(&child).unwrap().clone() + } + + pub fn build_graph(&self) -> (DataStructuresGraph, DefIdMap) { + let mut sg_graph: DataStructuresGraph = DataStructuresGraph::new(); + let mut nodes_idx = Default::default(); + for (key, val) in self.parent.iter() { + let idx = self.node_idx(&mut sg_graph, &mut nodes_idx, *key); + for parent in val { + let pidx = self.node_idx(&mut sg_graph, &mut nodes_idx, *parent); + sg_graph.add_edge(idx, pidx, format!("fromt {:?} to {:?}", key, parent)); + debug!("from {:?} to {:?}", key, parent); + } + } + + (sg_graph, nodes_idx) + } + + /// Return true if impl1 and impl2 are allowed to overlap: + /// They have an intersection impl + pub fn check_overlap( + &self, + sg_graph: &DataStructuresGraph, + nodes_idx: &DefIdMap, + impl1: DefId, + impl2: DefId, + ) -> bool { + let impl1_idx = *nodes_idx.get(&impl1).unwrap(); + let impl2_idx = *nodes_idx.get(&impl2).unwrap(); + + let impl1_incoming_nodes = sg_graph.nodes_in_postorder(INCOMING, impl1_idx); + let impl2_incoming_nodes = sg_graph.nodes_in_postorder(INCOMING, impl2_idx); + + debug!("check overlap {:?} {:?}", impl1, impl2); + debug!("impl1_incoming_nodes {:?}", impl1_incoming_nodes); + debug!("impl2_incoming_nodes {:?}", impl2_incoming_nodes); + + impl1_incoming_nodes[0] == impl2_incoming_nodes[0] } - /// The parent of a given impl, which is the def id of the trait when the - /// impl is a "specialization root". - pub fn parent(&self, child: DefId) -> DefId { - *self.parent.get(&child).unwrap() + fn node_idx( + &self, + sg_graph: &mut DataStructuresGraph, + nodes_idx: &mut DefIdMap, + node: DefId, + ) -> NodeIndex { + if nodes_idx.get(&node).is_some() { + *nodes_idx.get(&node).unwrap() + } else { + let idx = sg_graph.add_node(node); + nodes_idx.insert(node, idx); + idx + } } } @@ -332,22 +447,43 @@ impl<'a, 'gcx, 'tcx> Node { pub struct Ancestors { trait_def_id: DefId, specialization_graph: Lrc, - current_source: Option, + current_source: Option>, } impl Iterator for Ancestors { type Item = Node; fn next(&mut self) -> Option { + // Visit and return the graph nodes from bottom to top + // When multiple parents are found return each one of them + // prior to move up in the graph let cur = self.current_source.take(); - if let Some(Node::Impl(cur_impl)) = cur { - let parent = self.specialization_graph.parent(cur_impl); - if parent == self.trait_def_id { - self.current_source = Some(Node::Trait(parent)); - } else { - self.current_source = Some(Node::Impl(parent)); + match cur { + Some(mut cur_vec) => { + let next_value = cur_vec[0]; + if let Node::Impl(cur_impl) = next_value { + let parents = self.specialization_graph.parents(cur_impl); + + let mut ncur = vec![]; + ncur.append(&mut cur_vec[1..].to_vec()); + for parent in parents { + let node = if parent == self.trait_def_id { + Node::Trait(parent) + } else { + Node::Impl(parent) + }; + + if ncur.iter().find(|n| n.def_id() == node.def_id()).is_none() { + ncur.push(node); + } + } + + self.current_source = Some(ncur); + } + + Some(next_value) } + None => None, } - cur } } @@ -375,34 +511,39 @@ impl<'a, 'gcx, 'tcx> Ancestors { trait_item_name: Name, trait_item_kind: ty::AssociatedKind, trait_def_id: DefId, - ) -> impl Iterator> + Captures<'gcx> + Captures<'tcx> + 'a { + ) -> impl Iterator> + Captures<'gcx> + Captures<'tcx> + 'a + { self.flat_map(move |node| { - node.items(tcx).filter(move |impl_item| { - impl_item.kind == trait_item_kind && - tcx.hygienic_eq(impl_item.name, trait_item_name, trait_def_id) - }).map(move |item| NodeItem { node: node, item: item }) + node.items(tcx) + .filter(move |impl_item| { + impl_item.kind == trait_item_kind + && tcx.hygienic_eq(impl_item.name, trait_item_name, trait_def_id) + }) + .map(move |item| NodeItem { + node: node, + item: item, + }) }) } } /// Walk up the specialization ancestors of a given impl, starting with that /// impl itself. -pub fn ancestors(tcx: TyCtxt, - trait_def_id: DefId, - start_from_impl: DefId) - -> Ancestors { +pub fn ancestors(tcx: TyCtxt, trait_def_id: DefId, start_from_impl: DefId) -> Ancestors { let specialization_graph = tcx.specialization_graph_of(trait_def_id); Ancestors { trait_def_id, specialization_graph, - current_source: Some(Node::Impl(start_from_impl)), + current_source: Some(vec![Node::Impl(start_from_impl)]), } } impl<'a> HashStable> for Children { - fn hash_stable(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher) { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { let Children { ref nonblanket_impls, ref blanket_impls, diff --git a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs index 8e9d1eff34580..80e61d7b9b43a 100644 --- a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs +++ b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs @@ -21,6 +21,7 @@ impl !Send for TestType {} //~^ ERROR conflicting implementations of trait `std::marker::Send` unsafe impl Send for TestType {} +//~^ ERROR conflicting implementations of trait `std::marker::Send` impl !Send for TestType {} //~^ ERROR conflicting implementations of trait `std::marker::Send` diff --git a/src/test/compile-fail/specialization/intersection-impl.rs b/src/test/compile-fail/specialization/intersection-impl.rs new file mode 100644 index 0000000000000..de1df63abf9af --- /dev/null +++ b/src/test/compile-fail/specialization/intersection-impl.rs @@ -0,0 +1,36 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// test conflicting implementation error +// should an intersection impl help be shown? + +trait MyClone { + fn clone(&self) -> Self; +} + +impl MyClone for T { + fn clone(&self) -> T { + *self + } +} + +impl MyClone for Option { + fn clone(&self) -> Option { + match *self { + Some(ref v) => Some(v.clone()), + None => None, + } + } +} +//~^^^^^^^^ ERROR conflicting implementations of trait `MyClone` for type `std::option::Option<_>` + +fn main() {} diff --git a/src/test/run-pass/specialization/intersection-impl-2.rs b/src/test/run-pass/specialization/intersection-impl-2.rs new file mode 100644 index 0000000000000..a3229a7b310e5 --- /dev/null +++ b/src/test/run-pass/specialization/intersection-impl-2.rs @@ -0,0 +1,63 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +trait A { } +trait B { } +trait C { } + +trait MyTrait { + fn foo(&self) -> &'static str; +} + +impl MyTrait for T { + default fn foo(&self) -> &'static str { + "implements_A" + } +} +impl MyTrait for T { + default fn foo(&self) -> &'static str { + "implements_B" + } +} + +// This would be OK: +impl MyTrait for T { + default fn foo(&self) -> &'static str { + "implements_A+B" + } +} +// But what about this: +impl MyTrait for T { + fn foo(&self) -> &'static str { + "implements_A+B+C" + } +} + +struct S_A; +struct S_B; +struct S_AB; +struct S_ABC; + +impl A for S_A {} +impl B for S_B {} +impl A for S_AB {} +impl B for S_AB {} +impl A for S_ABC {} +impl B for S_ABC {} +impl C for S_ABC {} + +fn main() { + assert!(S_A.foo() == "implements_A_"); + assert!(S_B.foo() == "implements_B"); + assert!(S_AB.foo() == "implements_A+B"); + assert!(S_ABC.foo() == "implements_A+B+C"); +} diff --git a/src/test/run-pass/specialization/intersection-impl.rs b/src/test/run-pass/specialization/intersection-impl.rs new file mode 100644 index 0000000000000..eb9f0cb643d5a --- /dev/null +++ b/src/test/run-pass/specialization/intersection-impl.rs @@ -0,0 +1,42 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Test if two impls are allowed to overlap if a third +// impl is the intersection of them + +trait MyClone { + fn my_clone(&self) -> &'static str; +} + +impl MyClone for T { + default fn my_clone(&self) -> &'static str { + "impl_a" + } +} + +impl MyClone for Option { + default fn my_clone(&self) -> &'static str { + "impl_b" + } +} + +impl MyClone for Option { + fn my_clone(&self) -> &'static str { + "impl_c" + } +} + +fn main() { + assert!(42i32.my_clone() == "impl_a"); + assert!(Some(Box::new(42i32)).my_clone() == "impl_b"); + assert!(Some(42i32).my_clone() == "impl_c"); +} From 5067f542fe288c76c91feaa291a3b361aa886caa Mon Sep 17 00:00:00 2001 From: Gianni Ciccarelli Date: Tue, 29 May 2018 08:57:50 +0000 Subject: [PATCH 2/2] Intersection impls check if the intersection impl is complete --- src/librustc/traits/coherence.rs | 14 +- src/librustc/traits/specialize/mod.rs | 65 +++--- .../traits/specialize/specialization_graph.rs | 199 +++++++++++------- .../intersection-impl-not-complete.rs | 30 +++ .../specialization/intersection-impl-2.rs | 63 ------ src/test/ui/e0119/issue-28981.stderr | 4 +- 6 files changed, 197 insertions(+), 178 deletions(-) create mode 100644 src/test/compile-fail/specialization/intersection-impl-not-complete.rs delete mode 100644 src/test/run-pass/specialization/intersection-impl-2.rs diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index 1a763587f0eb7..31f8af1f96872 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -8,11 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! See `README.md` for high-level documentation +//! See rustc guide chapters on [trait-resolution] and [trait-specialization] for more info on how +//! this works. +//! +//! [trait-resolution]: https://rust-lang-nursery.github.io/rustc-guide/trait-resolution.html +//! [trait-specialization]: https://rust-lang-nursery.github.io/rustc-guide/trait-specialization.html use hir::def_id::{DefId, LOCAL_CRATE}; use syntax_pos::DUMMY_SP; -use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal}; +use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause}; use traits::IntercrateMode; use traits::select::IntercrateAmbiguityCause; use ty::{self, Ty, TyCtxt}; @@ -119,7 +123,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, // types into scope; instead, we replace the generic types with // fresh type variables, and hence we do our evaluations in an // empty environment. - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id); let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id); @@ -136,7 +140,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, Err(_) => return None }; - debug!("overlap: unification check succeeded {:?}", obligations); + debug!("overlap: unification check succeeded"); // Are any of the obligations unsatisfiable? If so, no overlap. let infcx = selcx.infcx(); @@ -277,7 +281,7 @@ pub fn orphan_check<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, /// is bad, because the only local type with `T` as a subtree is /// `LocalType`, and `Vec<->` is between it and the type parameter. /// - similarly, `FundamentalPair, T>` is bad, because -/// the second occurence of `T` is not a subtree of *any* local type. +/// the second occurrence of `T` is not a subtree of *any* local type. /// - however, `LocalType>` is OK, because `T` is a subtree of /// `LocalType>`, which is local and has no types between it and /// the type parameter. diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 11e7e4f6b9a1a..80beeafb072a8 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -339,10 +339,9 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx for opt_overlap in opt_overlaps { overlaps.push((impl_def_id, opt_overlap)); } - }, - _ => {}, + } + _ => {} }; - } else { let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); sg.record_impl_from_cstore(tcx, parent, impl_def_id) @@ -353,47 +352,55 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx // Build the graph only if there is at least an overlap let (graph, nodes_idx) = sg.build_graph(); for (impl_def_id, overlap) in overlaps { - if !sg.check_overlap(&graph, &nodes_idx, impl_def_id, overlap.with_impl) { - - let msg = format!("conflicting implementations of trait `{}`{}:{}", + if !sg.check_overlap(&graph, &nodes_idx, impl_def_id, overlap.with_impl, tcx) { + let msg = format!( + "conflicting implementations of trait `{}`{}:{}", overlap.trait_desc, - overlap.self_desc.clone().map_or( - String::new(), |ty| { - format!(" for type `{}`", ty) - }), - if overlap.used_to_be_allowed { " (E0119)" } else { "" } - ); - let impl_span = tcx.sess.codemap().def_span( - tcx.span_of_impl(impl_def_id).unwrap() + overlap + .self_desc + .clone() + .map_or(String::new(), |ty| format!(" for type `{}`", ty)), + if overlap.used_to_be_allowed { + " (E0119)" + } else { + "" + } ); + let impl_span = tcx.sess + .codemap() + .def_span(tcx.span_of_impl(impl_def_id).unwrap()); let mut err = if overlap.used_to_be_allowed { tcx.struct_span_lint_node( lint::builtin::INCOHERENT_FUNDAMENTAL_IMPLS, tcx.hir.as_local_node_id(impl_def_id).unwrap(), impl_span, - &msg) + &msg, + ) } else { - struct_span_err!(tcx.sess, - impl_span, - E0119, - "{}", - msg) + struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg) }; match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { - err.span_label(tcx.sess.codemap().def_span(span), - format!("first implementation here")); - err.span_label(impl_span, - format!("conflicting implementation{}", - overlap.self_desc - .map_or(String::new(), - |ty| format!(" for `{}`", ty)))); + err.span_label( + tcx.sess.codemap().def_span(span), + format!("first implementation here"), + ); + err.span_label( + impl_span, + format!( + "conflicting implementation{}", + overlap + .self_desc + .map_or(String::new(), |ty| format!(" for `{}`", ty)) + ), + ); } Err(cname) => { let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => format!( - "conflicting implementation in crate `{}`:\n- {}", cname, s), + Some(s) => { + format!("conflicting implementation in crate `{}`:\n- {}", cname, s) + } None => format!("conflicting implementation in crate `{}`", cname), }; err.note(&msg); diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index 590460b06c2a4..7d9dfa8d79626 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -12,15 +12,19 @@ use super::OverlapError; use hir::def_id::DefId; use ich::{self, StableHashingContext}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, + StableHasherResult}; use rustc_data_structures::graph::{Graph as DataStructuresGraph, NodeIndex, INCOMING}; -use traits; +use traits::{self, ObligationCause, TraitEngine}; use ty::{self, TyCtxt, TypeFoldable}; use ty::fast_reject::{self, SimplifiedType}; use rustc_data_structures::sync::Lrc; -use syntax::ast::Name; +use syntax::ast::{Name, DUMMY_NODE_ID}; use util::captures::Captures; use util::nodemap::{DefIdMap, FxHashMap}; +use super::FulfillmentContext; +use std::collections::HashSet; +use syntax_pos::DUMMY_SP; /// A per-trait graph of impls in specialization order. At the moment, this /// graph forms a tree rooted with the trait itself, with all other nodes @@ -88,13 +92,12 @@ impl<'a, 'gcx, 'tcx> Children { } /// Insert an impl into this set of children without comparing to any existing impls - fn insert_blindly(&mut self, tcx: TyCtxt<'a, 'gcx, 'tcx>, impl_def_id: DefId) { + fn insert_blindly(&mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - self.nonblanket_impls - .entry(sty) - .or_insert(vec![]) - .push(impl_def_id) + self.nonblanket_impls.entry(sty).or_insert(vec![]).push(impl_def_id) } else { self.blanket_impls.push(impl_def_id) } @@ -102,12 +105,12 @@ impl<'a, 'gcx, 'tcx> Children { /// Attempt to insert an impl into this set of children, while comparing for /// specialization relationships. - fn insert( - &mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - impl_def_id: DefId, - simplified_self: Option, - ) -> Result, OverlapError> { + fn insert(&mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + impl_def_id: DefId, + simplified_self: Option) + -> Result, OverlapError> + { let mut last_lint = None; // Nodes could specialize more than one parent @@ -165,18 +168,14 @@ impl<'a, 'gcx, 'tcx> Children { )?; if le && !ge { - debug!( - "descending as child of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap() - ); + debug!("descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap()); // the impl specializes possible_sibling inserted_results.push(Inserted::ShouldRecurseOn(possible_sibling)); } else if ge && !le { - debug!( - "placing as parent of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap() - ); + debug!("placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap()); // possible_sibling specializes the impl *slot = impl_def_id; @@ -209,18 +208,14 @@ impl<'a, 'gcx, 'tcx> Children { Ok(vec![Inserted::BecameNewSibling(last_lint)]) } - fn iter_mut(&'a mut self) -> Box + 'a> { - let nonblanket = self.nonblanket_impls - .iter_mut() - .flat_map(|(_, v)| v.iter_mut()); + fn iter_mut(&'a mut self) -> Box + 'a> { + let nonblanket = self.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter_mut()); Box::new(self.blanket_impls.iter_mut().chain(nonblanket)) } - fn filtered_mut(&'a mut self, sty: SimplifiedType) -> Box + 'a> { - let nonblanket = self.nonblanket_impls - .entry(sty) - .or_insert(vec![]) - .iter_mut(); + fn filtered_mut(&'a mut self, sty: SimplifiedType) + -> Box + 'a> { + let nonblanket = self.nonblanket_impls.entry(sty).or_insert(vec![]).iter_mut(); Box::new(self.blanket_impls.iter_mut().chain(nonblanket)) } } @@ -236,36 +231,29 @@ impl<'a, 'gcx, 'tcx> Graph { /// Insert a local impl into the specialization graph. If an existing impl /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. - pub fn insert( - &mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - impl_def_id: DefId, - ) -> Result>, OverlapError> { + pub fn insert(&mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + impl_def_id: DefId) + -> Result>, OverlapError> { assert!(impl_def_id.is_local()); let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; - debug!( - "insert({:?}): inserting TraitRef {:?} into specialization graph", - impl_def_id, trait_ref - ); + debug!("insert({:?}): inserting TraitRef {:?} into specialization graph", + impl_def_id, trait_ref); // if the reference itself contains an earlier error (e.g., due to a // resolution failure), then we just insert the impl at the top level of // the graph and claim that there's no overlap (in order to suppress // bogus errors). if trait_ref.references_error() { - debug!( - "insert: inserting dummy node for erroneous TraitRef {:?}, \ - impl_def_id={:?}, trait_def_id={:?}", - trait_ref, impl_def_id, trait_def_id - ); + debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \ + impl_def_id={:?}, trait_def_id={:?}", + trait_ref, impl_def_id, trait_def_id); self.parent_insert(impl_def_id, trait_def_id); - self.children - .entry(trait_def_id) - .or_insert(Children::new()) + self.children.entry(trait_def_id).or_insert(Children::new()) .insert_blindly(tcx, impl_def_id); return Ok(None); } @@ -333,12 +321,10 @@ impl<'a, 'gcx, 'tcx> Graph { } /// Insert cached metadata mapping from a child impl back to its parent. - pub fn record_impl_from_cstore( - &mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - parent: DefId, - child: DefId, - ) { + pub fn record_impl_from_cstore(&mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + parent: DefId, + child: DefId) { if self.parent_insert(child, parent).is_some() { bug!( "When recording an impl from the crate store, information about its parent \ @@ -346,10 +332,7 @@ impl<'a, 'gcx, 'tcx> Graph { ); } - self.children - .entry(parent) - .or_insert(Children::new()) - .insert_blindly(tcx, child); + self.children.entry(parent).or_insert(Children::new()).insert_blindly(tcx, child); } /// Returns the def-id of the parent impl(s) for a given impl. @@ -381,18 +364,81 @@ impl<'a, 'gcx, 'tcx> Graph { nodes_idx: &DefIdMap, impl1: DefId, impl2: DefId, + tcx: TyCtxt<'a, 'tcx, 'tcx>, ) -> bool { + let impl1_idx = *nodes_idx.get(&impl1).unwrap(); let impl2_idx = *nodes_idx.get(&impl2).unwrap(); + // two impls are allowed to overlap if they have a mutual postdominator in the graph. + // That postdominator is the specializing impl. let impl1_incoming_nodes = sg_graph.nodes_in_postorder(INCOMING, impl1_idx); let impl2_incoming_nodes = sg_graph.nodes_in_postorder(INCOMING, impl2_idx); - debug!("check overlap {:?} {:?}", impl1, impl2); - debug!("impl1_incoming_nodes {:?}", impl1_incoming_nodes); - debug!("impl2_incoming_nodes {:?}", impl2_incoming_nodes); + if impl1_incoming_nodes[0] == impl2_incoming_nodes[0] { + + // a mutual postdominator has been found but the intersection impls + // could not be complete. + // The query below tries to answer to the following + // Does the exist a set of types such that + // - impl A applies and impl B applies + // - but impl C does not apply + let mut result = true; + let int_impl = sg_graph.node_data(impl1_incoming_nodes[0]); + tcx.infer_ctxt().enter(|infcx| { + + let param_env1 = tcx.param_env(impl1); + let param_env2 = tcx.param_env(impl2); + + let mut combined_param_envs_vec = + param_env1 + .caller_bounds + .iter() + .chain(param_env2.caller_bounds.iter()) + .map(|p| *p) + .collect::>(); + + let combined_param_envs: HashSet<_> = + combined_param_envs_vec + .drain(..) + .collect(); + + // Combine the param envs of the overlapping impls into a single param env. + let param_env = ty::ParamEnv::new( + tcx.intern_predicates( + combined_param_envs + .into_iter() + .collect::>() + .as_slice() + ), + param_env1.reveal + ); + + let predicates = tcx.predicates_of(*int_impl); + let ty = tcx.type_of(*int_impl); + + let mut fulfill_cx = FulfillmentContext::new(); + for predicate in predicates.predicates { + if let ty::Predicate::Trait(trait_predicate) = predicate { + fulfill_cx.register_bound( + &infcx, + param_env, + ty, + trait_predicate.skip_binder().trait_ref.def_id, + ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID), + ); + } + } - impl1_incoming_nodes[0] == impl2_incoming_nodes[0] + fulfill_cx.select_all_or_error(&infcx).unwrap_or_else(|_| { + result = false; + }); + }); + + result + } else { + false + } } fn node_idx( @@ -511,25 +557,22 @@ impl<'a, 'gcx, 'tcx> Ancestors { trait_item_name: Name, trait_item_kind: ty::AssociatedKind, trait_def_id: DefId, - ) -> impl Iterator> + Captures<'gcx> + Captures<'tcx> + 'a - { + ) -> impl Iterator> + Captures<'gcx> + Captures<'tcx> + 'a { self.flat_map(move |node| { - node.items(tcx) - .filter(move |impl_item| { - impl_item.kind == trait_item_kind - && tcx.hygienic_eq(impl_item.name, trait_item_name, trait_def_id) - }) - .map(move |item| NodeItem { - node: node, - item: item, - }) + node.items(tcx).filter(move |impl_item| { + impl_item.kind == trait_item_kind && + tcx.hygienic_eq(impl_item.name, trait_item_name, trait_def_id) + }).map(move |item| NodeItem { node: node, item: item }) }) } } /// Walk up the specialization ancestors of a given impl, starting with that /// impl itself. -pub fn ancestors(tcx: TyCtxt, trait_def_id: DefId, start_from_impl: DefId) -> Ancestors { +pub fn ancestors(tcx: TyCtxt, + trait_def_id: DefId, + start_from_impl: DefId) + -> Ancestors { let specialization_graph = tcx.specialization_graph_of(trait_def_id); Ancestors { trait_def_id, @@ -539,11 +582,9 @@ pub fn ancestors(tcx: TyCtxt, trait_def_id: DefId, start_from_impl: DefId) -> An } impl<'a> HashStable> for Children { - fn hash_stable( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher, - ) { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { let Children { ref nonblanket_impls, ref blanket_impls, diff --git a/src/test/compile-fail/specialization/intersection-impl-not-complete.rs b/src/test/compile-fail/specialization/intersection-impl-not-complete.rs new file mode 100644 index 0000000000000..59b774c6168ea --- /dev/null +++ b/src/test/compile-fail/specialization/intersection-impl-not-complete.rs @@ -0,0 +1,30 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// test if the intersecion impl is complete + +trait A { } +trait B { } +trait C { } + +trait MyTrait { } + +impl MyTrait for T { } +impl MyTrait for T { } +//~^ ERROR conflicting implementations of trait `MyTrait` + +// This would be OK: +//impl MyTrait for T { } +// But what about this: +impl MyTrait for T { } + +fn main() {} diff --git a/src/test/run-pass/specialization/intersection-impl-2.rs b/src/test/run-pass/specialization/intersection-impl-2.rs deleted file mode 100644 index a3229a7b310e5..0000000000000 --- a/src/test/run-pass/specialization/intersection-impl-2.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(specialization)] - -trait A { } -trait B { } -trait C { } - -trait MyTrait { - fn foo(&self) -> &'static str; -} - -impl MyTrait for T { - default fn foo(&self) -> &'static str { - "implements_A" - } -} -impl MyTrait for T { - default fn foo(&self) -> &'static str { - "implements_B" - } -} - -// This would be OK: -impl MyTrait for T { - default fn foo(&self) -> &'static str { - "implements_A+B" - } -} -// But what about this: -impl MyTrait for T { - fn foo(&self) -> &'static str { - "implements_A+B+C" - } -} - -struct S_A; -struct S_B; -struct S_AB; -struct S_ABC; - -impl A for S_A {} -impl B for S_B {} -impl A for S_AB {} -impl B for S_AB {} -impl A for S_ABC {} -impl B for S_ABC {} -impl C for S_ABC {} - -fn main() { - assert!(S_A.foo() == "implements_A_"); - assert!(S_B.foo() == "implements_B"); - assert!(S_AB.foo() == "implements_A+B"); - assert!(S_ABC.foo() == "implements_A+B+C"); -} diff --git a/src/test/ui/e0119/issue-28981.stderr b/src/test/ui/e0119/issue-28981.stderr index afcbab4a5c6c0..9d600fcac07cf 100644 --- a/src/test/ui/e0119/issue-28981.stderr +++ b/src/test/ui/e0119/issue-28981.stderr @@ -1,11 +1,11 @@ -error[E0119]: conflicting implementations of trait `std::ops::Deref` for type `&_`: +error[E0119]: conflicting implementations of trait `std::ops::Deref` for type `std::cell::Ref<'_, _>`: --> $DIR/issue-28981.rs:15:1 | LL | impl Deref for Foo { } //~ ERROR must be used | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl<'a, T> std::ops::Deref for &'a T + - impl<'b, T> std::ops::Deref for std::cell::Ref<'b, T> where T: ?Sized; error[E0210]: type parameter `Foo` must be used as the type parameter for some local type (e.g. `MyStruct`)