From 736603424e48db41e1f2e3ad58e67809ab608a33 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 4 Dec 2015 10:57:02 -0800 Subject: [PATCH 01/37] Fix existing comment typo. --- src/librustc/middle/traits/select.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index fbfd4b67b5bd5..dbfb018894f03 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -836,8 +836,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(None); } - - // If there are *NO* candidates, that there are no impls -- + // If there are *NO* candidates, then there are no impls -- // that we know of, anyway. Note that in the case where there // are unbound type variables within the obligation, it might // be the case that you could still satisfy the obligation From 659ba09b2d9dfe1c9ae50d86ab87fc9acb55a03e Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 7 Dec 2015 16:59:26 -0800 Subject: [PATCH 02/37] Remove useless vector accumulation in `type_vars_for_defs` --- src/librustc/middle/infer/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index e671dd73431e6..e08167e7ce688 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -1025,8 +1025,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { substs: &mut Substs<'tcx>, defs: &[ty::TypeParameterDef<'tcx>]) { - let mut vars = Vec::with_capacity(defs.len()); - for def in defs.iter() { let default = def.default.map(|default| { type_variable::Default { @@ -1038,7 +1036,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty_var = self.next_ty_var_with_default(default); substs.types.push(space, ty_var); - vars.push(ty_var) } } From 8fe63e23421f66b730afdbd14c3ec90e39950288 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 18 Dec 2015 14:38:28 -0800 Subject: [PATCH 03/37] Add `default` as contextual keyword, and parse it for impl items. --- src/librustc_front/fold.rs | 1 + src/librustc_front/hir.rs | 15 +++- src/librustc_front/lowering.rs | 8 ++ src/libsyntax/ast.rs | 15 +++- src/libsyntax/ext/expand.rs | 1 + src/libsyntax/fold.rs | 1 + src/libsyntax/parse/parser.rs | 33 +++++++- src/libsyntax/parse/token.rs | 97 ++++++++++++----------- src/libsyntax_ext/deriving/generic/mod.rs | 2 + src/test/parse-fail/default.rs | 35 ++++++++ 10 files changed, 150 insertions(+), 58 deletions(-) create mode 100644 src/test/parse-fail/default.rs diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs index beedb3d70b699..6ae59122f71c4 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -839,6 +839,7 @@ pub fn noop_fold_impl_item(i: ImplItem, folder: &mut T) -> ImplItem { name: folder.fold_name(i.name), attrs: fold_attrs(i.attrs, folder), vis: i.vis, + defaultness: i.defaultness, node: match i.node { ImplItemKind::Const(ty, expr) => { ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr)) diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index cc7c0f7865ea5..939b527fad302 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -864,10 +864,10 @@ pub struct MethodSig { pub explicit_self: ExplicitSelf, } -/// Represents a method declaration in a trait declaration, possibly including -/// a default implementation A trait method is either required (meaning it -/// doesn't have an implementation, just a signature) or provided (meaning it -/// has a default implementation). +/// Represents an item declaration within a trait declaration, +/// possibly including a default implementation. A trait item is +/// either required (meaning it doesn't have an implementation, just a +/// signature) or provided (meaning it has a default implementation). #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct TraitItem { pub id: NodeId, @@ -889,6 +889,7 @@ pub struct ImplItem { pub id: NodeId, pub name: Name, pub vis: Visibility, + pub defaultness: Defaultness, pub attrs: HirVec, pub node: ImplItemKind, pub span: Span, @@ -1046,6 +1047,12 @@ pub enum Constness { NotConst, } +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum Defaultness { + Default, + Final, +} + impl fmt::Display for Unsafety { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(match *self { diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index 291df66755e7d..825ab3fbd4c82 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -756,6 +756,7 @@ pub fn lower_impl_item(lctx: &LoweringContext, i: &ImplItem) -> hir::ImplItem { name: i.ident.name, attrs: lower_attrs(lctx, &i.attrs), vis: lower_visibility(lctx, i.vis), + defaultness: lower_defaultness(lctx, i.defaultness), node: match i.node { ImplItemKind::Const(ref ty, ref expr) => { hir::ImplItemKind::Const(lower_ty(lctx, ty), lower_expr(lctx, expr)) @@ -1707,6 +1708,13 @@ pub fn lower_visibility(_lctx: &LoweringContext, v: Visibility) -> hir::Visibili } } +pub fn lower_defaultness(_lctx: &LoweringContext, d: Defaultness) -> hir::Defaultness { + match d { + Defaultness::Default => hir::Defaultness::Default, + Defaultness::Final => hir::Defaultness::Final, + } +} + pub fn lower_block_check_mode(lctx: &LoweringContext, b: &BlockCheckMode) -> hir::BlockCheckMode { match *b { BlockCheckMode::Default => hir::DefaultBlock, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index f7621b0131ad4..c830fed5db9a7 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1328,10 +1328,10 @@ pub struct MethodSig { pub explicit_self: ExplicitSelf, } -/// Represents a method declaration in a trait declaration, possibly including -/// a default implementation. A trait method is either required (meaning it -/// doesn't have an implementation, just a signature) or provided (meaning it -/// has a default implementation). +/// Represents an item declaration within a trait declaration, +/// possibly including a default implementation. A trait item is +/// either required (meaning it doesn't have an implementation, just a +/// signature) or provided (meaning it has a default implementation). #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct TraitItem { pub id: NodeId, @@ -1353,6 +1353,7 @@ pub struct ImplItem { pub id: NodeId, pub ident: Ident, pub vis: Visibility, + pub defaultness: Defaultness, pub attrs: Vec, pub node: ImplItemKind, pub span: Span, @@ -1654,6 +1655,12 @@ pub enum Constness { NotConst, } +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum Defaultness { + Default, + Final, +} + impl fmt::Display for Unsafety { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(match *self { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f5794f7219bcf..5bfdab791d638 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1061,6 +1061,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) ident: ii.ident, attrs: ii.attrs, vis: ii.vis, + defaultness: ii.defaultness, node: match ii.node { ast::ImplItemKind::Method(sig, body) => { let (sig, body) = expand_and_rename_method(sig, body, fld); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9056103d30086..cd8998a211ae7 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -993,6 +993,7 @@ pub fn noop_fold_impl_item(i: ImplItem, folder: &mut T) ident: folder.fold_ident(i.ident), attrs: fold_attrs(i.attrs, folder), vis: i.vis, + defaultness: i.defaultness, node: match i.node { ast::ImplItemKind::Const(ty, expr) => { ast::ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr)) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 5884be401503d..969d39056aa75 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -18,7 +18,7 @@ use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind}; use ast::Block; use ast::{BlockCheckMode, CaptureBy}; use ast::{Constness, Crate, CrateConfig}; -use ast::{Decl, DeclKind}; +use ast::{Decl, DeclKind, Defaultness}; use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{Expr, ExprKind, RangeLimits}; use ast::{Field, FnDecl}; @@ -644,6 +644,25 @@ impl<'a> Parser<'a> { } } + pub fn check_contextual_keyword(&mut self, ident: Ident) -> bool { + let tok = token::Ident(ident, token::Plain); + self.expected_tokens.push(TokenType::Token(tok)); + if let token::Ident(ref cur_ident, _) = self.token { + cur_ident.name == ident.name + } else { + false + } + } + + pub fn eat_contextual_keyword(&mut self, ident: Ident) -> PResult { + if self.check_contextual_keyword(ident) { + try!(self.bump()); + Ok(true) + } else { + Ok(false) + } + } + /// If the given word is not a keyword, signal an error. /// If the next token is not the given word, signal an error. /// Otherwise, eat it. @@ -705,7 +724,6 @@ impl<'a> Parser<'a> { } } - /// Attempt to consume a `<`. If `<<` is seen, replace it with a single /// `<` and continue. If a `<` is not seen, return false. /// @@ -4846,6 +4864,7 @@ impl<'a> Parser<'a> { let mut attrs = try!(self.parse_outer_attributes()); let lo = self.span.lo; let vis = try!(self.parse_visibility()); + let defaultness = try!(self.parse_defaultness()); let (name, node) = if self.eat_keyword(keywords::Type) { let name = try!(self.parse_ident()); try!(self.expect(&token::Eq)); @@ -4872,6 +4891,7 @@ impl<'a> Parser<'a> { span: mk_sp(lo, self.last_span.hi), ident: name, vis: vis, + defaultness: defaultness, attrs: attrs, node: node }) @@ -5208,6 +5228,15 @@ impl<'a> Parser<'a> { else { Ok(Visibility::Inherited) } } + /// Parse defaultness: DEFAULT or nothing + fn parse_defaultness(&mut self) -> PResult { + if try!(self.eat_contextual_keyword(special_idents::DEFAULT)) { + Ok(Defaultness::Default) + } else { + Ok(Defaultness::Final) + } + } + /// Given a termination token, parse all of the items in a module fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> { let mut items = vec![]; diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 294cbf358954f..033ac9440bcec 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -545,66 +545,67 @@ declare_special_idents_and_keywords! { (9, __unused1, "<__unused1>"); (super::SELF_TYPE_KEYWORD_NAME_NUM, type_self, "Self"); (11, prelude_import, "prelude_import"); + (12, DEFAULT, "default"); } pub mod keywords { // These ones are variants of the Keyword enum 'strict: - (12, As, "as"); - (13, Break, "break"); - (14, Crate, "crate"); - (15, Else, "else"); - (16, Enum, "enum"); - (17, Extern, "extern"); - (18, False, "false"); - (19, Fn, "fn"); - (20, For, "for"); - (21, If, "if"); - (22, Impl, "impl"); - (23, In, "in"); - (24, Let, "let"); - (25, Loop, "loop"); - (26, Match, "match"); - (27, Mod, "mod"); - (28, Move, "move"); - (29, Mut, "mut"); - (30, Pub, "pub"); - (31, Ref, "ref"); - (32, Return, "return"); + (13, As, "as"); + (14, Break, "break"); + (15, Crate, "crate"); + (16, Else, "else"); + (17, Enum, "enum"); + (18, Extern, "extern"); + (19, False, "false"); + (20, Fn, "fn"); + (21, For, "for"); + (22, If, "if"); + (23, Impl, "impl"); + (24, In, "in"); + (25, Let, "let"); + (26, Loop, "loop"); + (27, Match, "match"); + (28, Mod, "mod"); + (29, Move, "move"); + (30, Mut, "mut"); + (31, Pub, "pub"); + (32, Ref, "ref"); + (33, Return, "return"); // Static and Self are also special idents (prefill de-dupes) (super::STATIC_KEYWORD_NAME_NUM, Static, "static"); (super::SELF_KEYWORD_NAME_NUM, SelfValue, "self"); (super::SELF_TYPE_KEYWORD_NAME_NUM, SelfType, "Self"); - (33, Struct, "struct"); + (34, Struct, "struct"); (super::SUPER_KEYWORD_NAME_NUM, Super, "super"); - (34, True, "true"); - (35, Trait, "trait"); - (36, Type, "type"); - (37, Unsafe, "unsafe"); - (38, Use, "use"); - (39, While, "while"); - (40, Continue, "continue"); - (41, Box, "box"); - (42, Const, "const"); - (43, Where, "where"); + (35, True, "true"); + (36, Trait, "trait"); + (37, Type, "type"); + (38, Unsafe, "unsafe"); + (39, Use, "use"); + (40, While, "while"); + (41, Continue, "continue"); + (42, Box, "box"); + (43, Const, "const"); + (44, Where, "where"); 'reserved: - (44, Virtual, "virtual"); - (45, Proc, "proc"); - (46, Alignof, "alignof"); - (47, Become, "become"); - (48, Offsetof, "offsetof"); - (49, Priv, "priv"); - (50, Pure, "pure"); - (51, Sizeof, "sizeof"); - (52, Typeof, "typeof"); - (53, Unsized, "unsized"); - (54, Yield, "yield"); - (55, Do, "do"); - (56, Abstract, "abstract"); - (57, Final, "final"); - (58, Override, "override"); - (59, Macro, "macro"); + (45, Virtual, "virtual"); + (46, Proc, "proc"); + (47, Alignof, "alignof"); + (48, Become, "become"); + (49, Offsetof, "offsetof"); + (50, Priv, "priv"); + (51, Pure, "pure"); + (52, Sizeof, "sizeof"); + (53, Typeof, "typeof"); + (54, Unsized, "unsized"); + (55, Yield, "yield"); + (56, Do, "do"); + (57, Abstract, "abstract"); + (58, Final, "final"); + (59, Override, "override"); + (60, Macro, "macro"); } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index c0237a5d29a41..937055fcfa672 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -476,6 +476,7 @@ impl<'a> TraitDef<'a> { span: self.span, ident: ident, vis: ast::Visibility::Inherited, + defaultness: ast::Defaultness::Final, attrs: Vec::new(), node: ast::ImplItemKind::Type(type_def.to_ty(cx, self.span, @@ -893,6 +894,7 @@ impl<'a> MethodDef<'a> { attrs: self.attributes.clone(), span: trait_.span, vis: ast::Visibility::Inherited, + defaultness: ast::Defaultness::Final, ident: method_ident, node: ast::ImplItemKind::Method(ast::MethodSig { generics: fn_generics, diff --git a/src/test/parse-fail/default.rs b/src/test/parse-fail/default.rs new file mode 100644 index 0000000000000..d18401e67646c --- /dev/null +++ b/src/test/parse-fail/default.rs @@ -0,0 +1,35 @@ +// 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. + +// compile-flags: -Z parse-only + +// Test successful and unsucessful parsing of the `default` contextual keyword + +trait Foo { + fn foo() -> T; +} + +impl Foo for u8 { + default fn foo() -> T { + T::default() + } +} + +impl Foo for u16 { + pub default fn foo() -> T { + T::default() + } +} + +impl Foo for u32 { + default pub fn foo() -> T { T::default() } //~ ERROR expected one of +} + +fn main() {} From 991f32a6cae575755a6991f594da377e593c94f3 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 28 Dec 2015 16:14:28 -0800 Subject: [PATCH 04/37] Hook `default` keyword into metadata and carry data through to typeck --- src/librustc/middle/ty/mod.rs | 7 ++++++- src/librustc_metadata/common.rs | 2 ++ src/librustc_metadata/decoder.rs | 17 +++++++++++++++++ src/librustc_metadata/encoder.rs | 11 +++++++++++ src/librustc_typeck/collect.rs | 14 ++++++++++++-- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 2491da7f5f317..d6399befb517a 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -281,6 +281,7 @@ pub struct Method<'tcx> { pub fty: BareFnTy<'tcx>, pub explicit_self: ExplicitSelfCategory, pub vis: hir::Visibility, + pub defaultness: hir::Defaultness, pub def_id: DefId, pub container: ImplOrTraitItemContainer, } @@ -292,16 +293,18 @@ impl<'tcx> Method<'tcx> { fty: BareFnTy<'tcx>, explicit_self: ExplicitSelfCategory, vis: hir::Visibility, + defaultness: hir::Defaultness, def_id: DefId, container: ImplOrTraitItemContainer) -> Method<'tcx> { - Method { + Method { name: name, generics: generics, predicates: predicates, fty: fty, explicit_self: explicit_self, vis: vis, + defaultness: defaultness, def_id: def_id, container: container, } @@ -334,6 +337,7 @@ pub struct AssociatedConst<'tcx> { pub name: Name, pub ty: Ty<'tcx>, pub vis: hir::Visibility, + pub defaultness: hir::Defaultness, pub def_id: DefId, pub container: ImplOrTraitItemContainer, pub has_value: bool @@ -344,6 +348,7 @@ pub struct AssociatedType<'tcx> { pub name: Name, pub ty: Option>, pub vis: hir::Visibility, + pub defaultness: hir::Defaultness, pub def_id: DefId, pub container: ImplOrTraitItemContainer, } diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs index 991cbe137ecf9..5d65c7a5784f9 100644 --- a/src/librustc_metadata/common.rs +++ b/src/librustc_metadata/common.rs @@ -241,6 +241,8 @@ pub const tag_items_data_item_constness: usize = 0xa6; pub const tag_items_data_item_deprecation: usize = 0xa7; +pub const tag_items_data_item_defaultness: usize = 0xa8; + pub const tag_rustc_version: usize = 0x10f; pub fn rustc_version() -> String { format!( diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index dfc794dc5b874..e1a0edada6168 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -165,6 +165,19 @@ fn fn_constness(item: rbml::Doc) -> hir::Constness { } } +fn item_defaultness(item: rbml::Doc) -> hir::Defaultness { + match reader::maybe_get_doc(item, tag_items_data_item_defaultness) { + None => hir::Defaultness::Default, // should occur only for default impls on traits + Some(defaultness_doc) => { + match reader::doc_as_u8(defaultness_doc) as char { + 'd' => hir::Defaultness::Default, + 'f' => hir::Defaultness::Final, + _ => panic!("unknown defaultness character") + } + } + } +} + fn item_sort(item: rbml::Doc) -> Option { reader::tagged_docs(item, tag_item_trait_item_sort).nth(0).map(|doc| { doc.as_str_slice().as_bytes()[0] as char @@ -976,6 +989,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, let name = item_name(&intr, item_doc); let vis = item_visibility(item_doc); + let defaultness = item_defaultness(item_doc); match item_sort(item_doc) { sort @ Some('C') | sort @ Some('c') => { @@ -984,6 +998,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, name: name, ty: ty, vis: vis, + defaultness: defaultness, def_id: def_id, container: container, has_value: sort == Some('C') @@ -1007,6 +1022,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, fty, explicit_self, vis, + defaultness, def_id, container))) } @@ -1016,6 +1032,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, name: name, ty: ty, vis: vis, + defaultness: defaultness, def_id: def_id, container: container, })) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 3dc0e53b254c4..d08cde2df1c04 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -451,6 +451,14 @@ fn encode_constness(rbml_w: &mut Encoder, constness: hir::Constness) { rbml_w.end_tag(); } +fn encode_defaultness(rbml_w: &mut Encoder, defaultness: hir::Defaultness) { + let ch = match defaultness { + hir::Defaultness::Default => 'd', + hir::Defaultness::Final => 'f', + }; + rbml_w.wr_tagged_u8(tag_items_data_item_defaultness, ch as u8); +} + fn encode_explicit_self(rbml_w: &mut Encoder, explicit_self: &ty::ExplicitSelfCategory) { let tag = tag_item_trait_method_explicit_self; @@ -674,6 +682,7 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, if let Some(ii) = impl_item_opt { encode_attributes(rbml_w, &ii.attrs); + encode_defaultness(rbml_w, ii.defaultness); encode_inlined_item(ecx, rbml_w, InlinedItemRef::ImplItem(ecx.tcx.map.local_def_id(parent_id), @@ -725,6 +734,7 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, impl_item)); } encode_constness(rbml_w, sig.constness); + encode_defaultness(rbml_w, impl_item.defaultness); if !any_types { let m_id = ecx.local_id(m.def_id); encode_symbol(ecx, rbml_w, m_id); @@ -767,6 +777,7 @@ fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, if let Some(ii) = impl_item_opt { encode_attributes(rbml_w, &ii.attrs); + encode_defaultness(rbml_w, ii.defaultness); } else { encode_predicates(rbml_w, ecx, index, &ecx.tcx.lookup_predicates(associated_type.def_id), diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b97d3f4993a40..69b84784eb614 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -533,6 +533,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, id: ast::NodeId, vis: hir::Visibility, sig: &hir::MethodSig, + defaultness: hir::Defaultness, untransformed_rcvr_ty: Ty<'tcx>, rcvr_ty_generics: &ty::Generics<'tcx>, rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) { @@ -554,6 +555,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fty, explicit_self_category, vis, + defaultness, def_id, container); @@ -600,6 +602,7 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, name: ast::Name, id: ast::NodeId, vis: hir::Visibility, + defaultness: hir::Defaultness, ty: ty::Ty<'tcx>, has_value: bool) { @@ -611,6 +614,7 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let associated_const = Rc::new(ty::AssociatedConst { name: name, vis: vis, + defaultness: defaultness, def_id: ccx.tcx.map.local_def_id(id), container: container, ty: ty, @@ -625,11 +629,13 @@ fn convert_associated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, name: ast::Name, id: ast::NodeId, vis: hir::Visibility, + defaultness: hir::Defaultness, ty: Option>) { let associated_type = Rc::new(ty::AssociatedType { name: name, vis: vis, + defaultness: defaultness, ty: ty, def_id: ccx.tcx.map.local_def_id(id), container: container @@ -767,6 +773,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_associated_const(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, impl_item.vis.inherit_from(parent_visibility), + impl_item.defaultness, ty, true /* has_value */); } } @@ -783,7 +790,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_associated_type(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, impl_item.vis, - Some(typ)); + impl_item.defaultness, Some(typ)); } } @@ -797,7 +804,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_method(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, method_vis, - sig, selfty, &ty_generics, &ty_predicates); + sig, impl_item.defaultness, selfty, &ty_generics, &ty_predicates); } } @@ -831,6 +838,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.name, trait_item.id, hir::Public, + hir::Defaultness::Default, ty, default.is_some()) } @@ -848,6 +856,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.name, trait_item.id, hir::Public, + hir::Defaultness::Default, typ); } } @@ -861,6 +870,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.id, hir::Inherited, sig, + hir::Defaultness::Default tcx.mk_self_type(), &trait_def.generics, &trait_predicates); From 45f4bf112a422fe62b9152ecf557f8550eb9c7ca Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 30 Dec 2015 13:47:23 -0800 Subject: [PATCH 05/37] Refactor `impl_trait_ref_and_oblig`, making it generally available as a utility --- src/librustc/middle/traits/coherence.rs | 4 +-- src/librustc/middle/traits/util.rs | 36 +++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 6005d36ff4eb2..454698c1b3a83 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -10,8 +10,8 @@ //! See `README.md` for high-level documentation -use super::{SelectionContext}; -use super::{Obligation, ObligationCause}; +use super::{SelectionContext, Obligation, ObligationCause}; +use super::util; use middle::cstore::LOCAL_CRATE; use middle::def_id::DefId; diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 08d504143c7cc..3289d58778290 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -10,13 +10,13 @@ use middle::def_id::DefId; use middle::infer::InferCtxt; -use middle::subst::Substs; +use middle::subst::{Subst, Substs}; use middle::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef}; use syntax::codemap::Span; use util::common::ErrorReported; use util::nodemap::FnvHashSet; -use super::{Obligation, ObligationCause, PredicateObligation}; +use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext, Normalized}; struct PredicateSet<'a,'tcx:'a> { tcx: &'a TyCtxt<'tcx>, @@ -299,6 +299,38 @@ impl<'tcx,I:Iterator>> Iterator for FilterToTraits { // Other /////////////////////////////////////////////////////////////////////////// +/// Instantiate all bound parameters of the impl with the given substs, +/// returning the resulting trait ref and all obligations that arise. +/// The obligations are closed under normalization. +pub fn impl_trait_ref_and_oblig<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, + impl_def_id: DefId, + impl_substs: &Substs<'tcx>) + -> (ty::TraitRef<'tcx>, + Vec>) +{ + let impl_trait_ref = + selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); + let impl_trait_ref = + impl_trait_ref.subst(selcx.tcx(), impl_substs); + let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = + super::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref); + + let predicates = selcx.tcx().lookup_predicates(impl_def_id); + let predicates = predicates.instantiate(selcx.tcx(), impl_substs); + let Normalized { value: predicates, obligations: normalization_obligations2 } = + super::normalize(selcx, ObligationCause::dummy(), &predicates); + let impl_obligations = + predicates_for_generics(ObligationCause::dummy(), 0, &predicates); + + let impl_obligations: Vec<_> = + impl_obligations.into_iter() + .chain(normalization_obligations1) + .chain(normalization_obligations2) + .collect(); + + (impl_trait_ref, impl_obligations) +} + // determine the `self` type, using fresh variables for all variables // declared on the impl declaration e.g., `impl for Box<[(A,B)]>` // would return ($0, $1) where $0 and $1 are freshly instantiated type From c5849e4dff28784ec0c3d5ace090b1da9439ec1f Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 22 Dec 2015 10:17:59 -0800 Subject: [PATCH 06/37] Add specialization module. The module contains a few important components: - The `specialize` function, which determines whether one impl is a specialization of another. - The `SpecializationGraph`, a per-trait graph recording the specialization tree. The main purpose of the graph is to allow traversals upwards (to less specialized impls) for discovering un-overridden defaults, and for ensuring that overridden items are allowed to be overridden. --- src/librustc/middle/traits/specialize.rs | 232 +++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 src/librustc/middle/traits/specialize.rs diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs new file mode 100644 index 0000000000000..ad8d6c4f95661 --- /dev/null +++ b/src/librustc/middle/traits/specialize.rs @@ -0,0 +1,232 @@ +// 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. + +// Logic and data structures related to impl specialization, explained in +// greater detail below. +// +// At the moment, this implementation support only the simple "chain" rule: +// If any two impls overlap, one must be a strict subset of the other. + +use super::util; +use super::SelectionContext; + +use middle::cstore::CrateStore; +use middle::def_id::DefId; +use middle::infer::{self, InferCtxt, TypeOrigin}; +use middle::region; +use middle::subst::{Subst, Substs}; +use middle::traits; +use middle::ty; +use syntax::codemap::DUMMY_SP; +use util::nodemap::DefIdMap; + +/// A per-trait graph of impls in specialization order. +/// +/// The graph provides two key services: +/// +/// - Construction, which implicitly checks for overlapping impls (i.e., impls +/// that overlap but where neither specializes the other -- an artifact of the +/// simple "chain" rule. +/// +/// - Parent extraction. In particular, the graph can give you the *immediate* +/// parents of a given specializing impl, which is needed for extracting +/// default items amongst other thigns. In the simple "chain" rule, every impl +/// has at most one parent. +pub struct SpecializationGraph { + // all impls have a parent; the "root" impls have as their parent the def_id + // of the trait + parent: DefIdMap, + + // the "root" impls are found by looking up the trait's def_id. + children: DefIdMap>, +} + +/// Information pertinent to an overlapping impl error. +pub struct Overlap<'tcx> { + pub with_impl: DefId, + pub on_trait_ref: ty::TraitRef<'tcx>, +} + +impl SpecializationGraph { + pub fn new() -> SpecializationGraph { + SpecializationGraph { + parent: Default::default(), + children: Default::default(), + } + } + + /// 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<'tcx>(&mut self, + tcx: &ty::ctxt<'tcx>, + impl_def_id: DefId, + trait_ref: ty::TraitRef) + -> Result<(), Overlap<'tcx>> { + assert!(impl_def_id.is_local()); + + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); + let mut parent = trait_ref.def_id; + + let mut my_children = vec![]; + + // descend the existing tree, looking for the right location to add this impl + 'descend: loop { + let mut possible_siblings = self.children.entry(parent).or_insert(vec![]); + + for slot in possible_siblings.iter_mut() { + let possible_sibling = *slot; + + let overlap = infcx.probe(|_| { + traits::overlapping_impls(&infcx, possible_sibling, impl_def_id) + }); + + if let Some(trait_ref) = overlap { + let le = specializes(&infcx, impl_def_id, possible_sibling); + let ge = specializes(&infcx, possible_sibling, impl_def_id); + + if le && !ge { + // the impl specializes possible_sibling + parent = possible_sibling; + continue 'descend; + } else if ge && !le { + // possible_sibling specializes the impl + *slot = impl_def_id; + self.parent.insert(possible_sibling, impl_def_id); + my_children.push(possible_sibling); + } else { + // overlap, but no specialization; error out + return Err(Overlap { + with_impl: possible_sibling, + on_trait_ref: trait_ref, + }); + } + + break 'descend; + } + } + + // no overlap with any potential siblings, so add as a new sibling + self.parent.insert(impl_def_id, parent); + possible_siblings.push(impl_def_id); + break; + } + + if self.children.insert(impl_def_id, my_children).is_some() { + panic!("When inserting an impl into the specialization graph, existing children for \ + the impl were already present."); + } + + Ok(()) + } + + /// Insert cached metadata mapping from a child impl back to its parent + pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) { + if self.parent.insert(child, Some(parent)).is_some() { + panic!("When recording an impl from the crate store, information about its parent \ + was already present."); + } + + self.children.entry(parent).or_insert(vec![]).push(child); + } +} + +fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { + let impl_generics = tcx.lookup_item_type(impl_def_id).generics; + + let types = impl_generics.types.map(|def| tcx.mk_param_from_def(def)); + + // FIXME: figure out what we actually want here + let regions = impl_generics.regions.map(|_| ty::Region::ReStatic); + // |d| infcx.next_region_var(infer::RegionVariableOrigin::EarlyBoundRegion(span, d.name))); + + Substs::new(types, regions) +} + +/// Is impl1 a specialization of impl2? +/// +/// Specialization is determined by the sets of types to which the impls apply; +/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies +/// to. +pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { + let tcx = &infcx.tcx; + + // We determine whether there's a subset relationship by: + // + // - skolemizing impl1, + // - assuming the where clauses for impl1, + // - unifying, + // - attempting to prove the where clauses for impl2 + // + // See RFC 1210 for more details and justification. + + let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id); + let (impl1_trait_ref, impl1_obligations) = { + let selcx = &mut SelectionContext::new(&infcx); + util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) + }; + + let impl1_predicates: Vec<_> = impl1_obligations.iter() + .cloned() + .map(|oblig| oblig.predicate) + .collect(); + + let penv = ty::ParameterEnvironment { + tcx: tcx, + free_substs: impl1_substs, + implicit_region_bound: ty::ReEmpty, // FIXME: is this OK? + caller_bounds: impl1_predicates, + selection_cache: traits::SelectionCache::new(), + evaluation_cache: traits::EvaluationCache::new(), + free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK? + }; + + // FIXME: unclear what `errors_will_be_reported` should be here... + let infcx = infer::new_infer_ctxt(tcx, infcx.tables, Some(penv), true); + let selcx = &mut SelectionContext::new(&infcx); + + let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); + let (impl2_trait_ref, impl2_obligations) = + util::impl_trait_ref_and_oblig(selcx, impl2_def_id, &impl2_substs); + + // do the impls unify? If not, no specialization. + if let Err(_) = infer::mk_eq_trait_refs(&infcx, + true, + TypeOrigin::Misc(DUMMY_SP), + impl1_trait_ref, + impl2_trait_ref) { + debug!("specializes: {:?} does not unify with {:?}", + impl1_trait_ref, + impl2_trait_ref); + return false; + } + + let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); + + // attempt to prove all of the predicates for impl2 given those for impl1 + // (which are packed up in penv) + for oblig in impl2_obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); + } + + if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { + debug!("specializes: for impls on {:?} and {:?}, could not fulfill: {:?} given {:?}", + impl1_trait_ref, + impl2_trait_ref, + errors, + infcx.parameter_environment.caller_bounds); + return false; + } + + debug!("specializes: an impl for {:?} specializes {:?} (`where` clauses elided)", + impl1_trait_ref, + impl2_trait_ref); + true +} From 1f34086e947139d498847dce49ba437748f17c2d Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 22 Dec 2015 10:20:47 -0800 Subject: [PATCH 07/37] Initial incorporation of specialization: - Rewrites the overlap checker to instead build up a specialization graph, checking for overlap errors in the process. - Use the specialization order during impl selection. This commit does not yet handle associated types correctly, and assumes that all items are `default` and are overridden. --- src/librustc/dep_graph/README.md | 9 +- src/librustc/middle/cstore.rs | 2 + src/librustc/middle/traits/coherence.rs | 9 +- src/librustc/middle/traits/mod.rs | 2 + src/librustc/middle/traits/select.rs | 109 +++++++---- src/librustc/middle/traits/util.rs | 1 - src/librustc/middle/ty/mod.rs | 7 +- src/librustc/middle/ty/sty.rs | 7 + src/librustc/middle/ty/trait_def.rs | 73 ++++++-- src/librustc_metadata/common.rs | 2 + src/librustc_metadata/csearch.rs | 5 + src/librustc_metadata/decoder.rs | 8 + src/librustc_metadata/encoder.rs | 12 +- src/librustc_typeck/coherence/mod.rs | 3 +- src/librustc_typeck/coherence/overlap.rs | 220 +++++++---------------- src/librustc_typeck/collect.rs | 1 + src/librustc_typeck/diagnostics.rs | 3 +- 17 files changed, 254 insertions(+), 219 deletions(-) diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md index 21742d9935dc2..ece5819829baa 100644 --- a/src/librustc/dep_graph/README.md +++ b/src/librustc/dep_graph/README.md @@ -51,7 +51,7 @@ could invalidate work done for other items. So, for example: not shared state, because if it changes it does not itself invalidate other functions (though it may be that it causes new monomorphizations to occur, but that's handled independently). - + Put another way: if the HIR for an item changes, we are going to recompile that item for sure. But we need the dep tracking map to tell us what *else* we have to recompile. Shared state is anything that is @@ -177,7 +177,7 @@ reads from `item`, there would be missing edges in the graph: | ^ | | +---------------------------------+ // added by `visit_all_items_in_krate` - + In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only present because we called `read` ourselves when entering the `ItemSignature(X)` task. @@ -273,8 +273,8 @@ should not exist. In contrast, using the memoized helper, you get: ... -> MapVariant(key) -> A | +----------> B - -which is much cleaner. + +which is much cleaner. **Be aware though that the closure is executed with `MapVariant(key)` pushed onto the stack as the current task!** That means that you must @@ -387,4 +387,3 @@ RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar' This will dump out all the nodes that lead from `Hir(foo)` to `TypeckItemBody(bar)`, from which you can (hopefully) see the source of the erroneous edge. - diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 193492ee7e10c..6a4680ecbaf31 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -176,6 +176,7 @@ pub trait CrateStore<'tcx> : Any { -> Option; fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId) -> Vec>>; + fn impl_parent(&self, impl_def_id: DefId) -> Option; // trait/impl-item info fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) @@ -346,6 +347,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { { unimplemented!() } fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId) -> Vec>> { unimplemented!() } + fn impl_parent(&self, def: DefId) -> Option { unimplemented!() } // trait/impl-item info fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 454698c1b3a83..00c72d0b48ab0 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -23,8 +23,8 @@ use syntax::codemap::DUMMY_SP; #[derive(Copy, Clone)] struct InferIsLocal(bool); -/// If there are types that satisfy both impls, returns an `ImplTy` -/// with those types substituted (by updating the given `infcx`) +/// If there are types that satisfy both impls, returns a suitably-freshened +/// `ImplHeader` with those types substituted pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>, impl1_def_id: DefId, impl2_def_id: DefId) @@ -85,7 +85,10 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>, return None } - Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header)) + let substituted = selcx.infcx().resolve_type_vars_if_possible(&a_impl_header); + let freshened = selcx.infcx().freshen(substituted); + + Some(freshened) } pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 8a2f0c0c09304..cbb0cc5aed704 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -50,6 +50,7 @@ pub use self::select::SelectionContext; pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants +pub use self::specialize::{Overlap, SpecializationGraph, specializes}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; @@ -67,6 +68,7 @@ mod fulfill; mod project; mod object_safety; mod select; +mod specialize; mod structural_impls; mod util; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index dbfb018894f03..a5fe1191d3f0c 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -40,6 +40,7 @@ use middle::infer; use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin}; use middle::subst::{Subst, Substs, TypeSpace}; use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; +use middle::traits; use middle::ty::fast_reject; use middle::ty::relate::TypeRelation; @@ -224,6 +225,12 @@ struct SelectionCandidateSet<'tcx> { ambiguous: bool, } +#[derive(PartialEq,Eq,Debug,Clone)] +struct EvaluatedCandidate<'tcx> { + candidate: SelectionCandidate<'tcx>, + evaluation: EvaluationResult, +} + enum BuiltinBoundConditions<'tcx> { If(ty::Binder>>), ParameterBuiltin, @@ -746,6 +753,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidate } + // Treat negative impls as unimplemented + fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>) + -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let ImplCandidate(def_id) = candidate { + if self.tcx().trait_impl_polarity(def_id) == Some(hir::ImplPolarity::Negative) { + return Err(Unimplemented) + } + } + Ok(Some(candidate)) + } + fn candidate_from_obligation_no_cache<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> @@ -803,12 +821,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // we were to winnow, we'd wind up with zero candidates. // Instead, we select the right impl now but report `Bar does // not implement Clone`. - if candidates.len() > 1 { - candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply()) - } + if candidates.len() == 1 { + return self.filter_negative_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. + let mut candidates: Vec<_> = candidates.into_iter().filter_map(|c| { + let eval = self.evaluate_candidate(stack, &c); + if eval.may_apply() { + Some(EvaluatedCandidate { + candidate: c, + evaluation: eval, + }) + } else { + None + } + }).collect(); - // If there are STILL multiple candidate, we can further reduce - // the list by dropping duplicates. + // If there are STILL multiple candidate, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. if candidates.len() > 1 { let mut i = 0; while i < candidates.len() { @@ -850,19 +883,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // Just one candidate left. - let candidate = candidates.pop().unwrap(); - - match candidate { - ImplCandidate(def_id) => { - match self.tcx().trait_impl_polarity(def_id) { - Some(hir::ImplPolarity::Negative) => return Err(Unimplemented), - _ => {} - } - } - _ => {} - } - - Ok(Some(candidate)) + self.filter_negative_impls(candidates.pop().unwrap().candidate) } fn is_knowable<'o>(&mut self, @@ -1564,41 +1585,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// candidates and prefer where-clause candidates. /// /// See the comment for "SelectionCandidate" for more details. - fn candidate_should_be_dropped_in_favor_of<'o>(&mut self, - victim: &SelectionCandidate<'tcx>, - other: &SelectionCandidate<'tcx>) - -> bool + fn candidate_should_be_dropped_in_favor_of<'o>( + &mut self, + victim: &EvaluatedCandidate<'tcx>, + other: &EvaluatedCandidate<'tcx>) + -> bool { - if victim == other { + if victim.candidate == other.candidate { return true; } - match other { - &ObjectCandidate | - &ParamCandidate(_) | &ProjectionCandidate => match victim { - &DefaultImplCandidate(..) => { + match other.candidate { + ObjectCandidate | + ParamCandidate(_) | ProjectionCandidate => match victim.candidate { + DefaultImplCandidate(..) => { self.tcx().sess.bug( "default implementations shouldn't be recorded \ when there are other valid candidates"); } - &ImplCandidate(..) | - &ClosureCandidate(..) | - &FnPointerCandidate | - &BuiltinObjectCandidate | - &BuiltinUnsizeCandidate | - &DefaultImplObjectCandidate(..) | - &BuiltinCandidate(..) => { + ImplCandidate(..) | + ClosureCandidate(..) | + FnPointerCandidate | + BuiltinObjectCandidate | + BuiltinUnsizeCandidate | + DefaultImplObjectCandidate(..) | + BuiltinCandidate(..) => { // We have a where-clause so don't go around looking // for impls. true } - &ObjectCandidate | - &ProjectionCandidate => { + ObjectCandidate | + ProjectionCandidate => { // Arbitrarily give param candidates priority // over projection and object candidates. true }, - &ParamCandidate(..) => false, + ParamCandidate(..) => false, + ErrorCandidate => false // propagate errors + }, + ImplCandidate(other_def) => { + // See if we can toss out `victim` based on specialization. + // This requires us to know *for sure* that the `other` impl applies + // i.e. EvaluatedToOk: + if other.evaluation == EvaluatedToOk { + if let ImplCandidate(victim_def) = victim.candidate { + return traits::specializes(self.infcx(), other_def, victim_def); + } + } + + false }, _ => false } diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 3289d58778290..1e37600bc0449 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -381,7 +381,6 @@ pub fn trait_ref_for_builtin_bound<'tcx>( } } - pub fn predicate_for_trait_ref<'tcx>( cause: ObligationCause<'tcx>, trait_ref: ty::TraitRef<'tcx>, diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index d6399befb517a..c336fd558bd56 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -2456,8 +2456,13 @@ impl<'tcx> TyCtxt<'tcx> { for impl_def_id in self.sess.cstore.implementations_of_trait(trait_id) { let impl_items = self.sess.cstore.impl_items(impl_def_id); let trait_ref = self.impl_trait_ref(impl_def_id).unwrap(); + // Record the trait->implementation mapping. - def.record_impl(self, impl_def_id, trait_ref); + if let Some(parent) = self.sess.cstore.impl_parent(impl_def_id) { + def.record_remote_impl(self, impl_def_id, trait_ref, parent); + } else { + def.record_remote_impl(self, impl_def_id, trait_ref, trait_id); + } // For any methods that use a default implementation, add them to // the map. This is a bit unfortunate. diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs index 2d7b7dc6e9b58..bbc5948f2cac7 100644 --- a/src/librustc/middle/ty/sty.rs +++ b/src/librustc/middle/ty/sty.rs @@ -1106,6 +1106,13 @@ impl<'tcx> TyS<'tcx> { } } + pub fn has_concrete_skeleton(&self) -> bool { + match self.sty { + TyParam(_) | TyInfer(_) | TyError => false, + _ => true, + } + } + // Returns the type and mutability of *ty. // // The parameter `explicit` indicates if this is an *explicit* dereference. diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index 5ecbbbcfbde6d..a67b2d9fb1cfc 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -59,6 +59,9 @@ pub struct TraitDef<'tcx> { /// Blanket impls associated with the trait. blanket_impls: RefCell>, + /// The specialization order for impls of this trait. + pub specialization_graph: RefCell, + /// Various flags pub flags: Cell } @@ -78,7 +81,8 @@ impl<'tcx> TraitDef<'tcx> { associated_type_names: associated_type_names, nonblanket_impls: RefCell::new(FnvHashMap()), blanket_impls: RefCell::new(vec![]), - flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) + flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS), + specialization_graph: RefCell::new(traits::SpecializationGraph::new()), } } @@ -114,42 +118,80 @@ impl<'tcx> TraitDef<'tcx> { tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id)); } - /// Records a trait-to-implementation mapping. - pub fn record_impl(&self, - tcx: &TyCtxt<'tcx>, - impl_def_id: DefId, - impl_trait_ref: ty::TraitRef<'tcx>) { + /// Records a basic trait-to-implementation mapping. + /// + /// Returns `true` iff the impl has not previously been recorded. + fn record_impl(&self, + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>) -> bool { debug!("TraitDef::record_impl for {:?}, from {:?}", self, impl_trait_ref); - // Record the write into the impl set, but only for local - // impls: external impls are handled differently. - if impl_def_id.is_local() { - self.write_trait_impls(tcx); - } - // We don't want to borrow_mut after we already populated all impls, // so check if an impl is present with an immutable borrow first. if let Some(sty) = fast_reject::simplify_type(tcx, impl_trait_ref.self_ty(), false) { if let Some(is) = self.nonblanket_impls.borrow().get(&sty) { if is.contains(&impl_def_id) { - return // duplicate - skip + return false; // duplicate - skip } } self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id) } else { if self.blanket_impls.borrow().contains(&impl_def_id) { - return // duplicate - skip + return false; // duplicate - skip } self.blanket_impls.borrow_mut().push(impl_def_id) } + + true + } + + /// Records a trait-to-implementation mapping for a crate-local impl. + pub fn record_local_impl(&self, + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>) { + self.record_impl(tcx, impl_def_id, impl_trait_ref); + } + + /// Records a trait-to-implementation mapping for a non-local impl. + /// + /// The `parent_impl` is the immediately-less-specialized impl, or the + /// trait's def ID if the impl is maximally-specialized -- information that + /// should be pulled from the metadata. + pub fn record_remote_impl(&self, + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>, + parent_impl: DefId) { + // if the impl has not previously been recorded + if self.record_impl(tcx, impl_def_id, impl_trait_ref) { + // if the impl is non-local, it's placed directly into the + // specialization graph using parent information drawn from metadata. + self.specialization_graph.borrow_mut() + .record_impl_from_cstore(parent_impl, impl_def_id) + } + } + + /// Adds a local impl into the specialization graph, returning an error with + /// overlap information if the impl overlaps but does not specialize an + /// existing impl. + pub fn add_impl_for_specialization(&self, + tcx: &ctxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>) + -> Result<(), traits::Overlap<'tcx>> { + assert!(impl_def_id.is_local()); + + self.specialization_graph.borrow_mut() + .insert(tcx, impl_def_id, impl_trait_ref) } pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { self.read_trait_impls(tcx); - tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); for &impl_def_id in self.blanket_impls.borrow().iter() { @@ -223,4 +265,3 @@ bitflags! { const IMPLS_VALID = 1 << 3, } } - diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs index 5d65c7a5784f9..a0cbba279acc0 100644 --- a/src/librustc_metadata/common.rs +++ b/src/librustc_metadata/common.rs @@ -243,6 +243,8 @@ pub const tag_items_data_item_deprecation: usize = 0xa7; pub const tag_items_data_item_defaultness: usize = 0xa8; +pub const tag_items_data_parent_impl: usize = 0xa9; + pub const tag_rustc_version: usize = 0x10f; pub fn rustc_version() -> String { format!( diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index b3f24b8f16b16..2cd119cfc48be 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -225,6 +225,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { decoder::get_associated_consts(self.intr.clone(), &cdata, def.index, tcx) } + fn impl_parent(&self, impl_def: DefId) -> Option { + let cdata = self.get_crate_data(impl_def.krate); + decoder::get_parent_impl(&*cdata, impl_def.index) + } + fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) -> Option { let cdata = self.get_crate_data(def_id.krate); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index e1a0edada6168..cf65d7a522ad1 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -34,6 +34,7 @@ use middle::lang_items; use middle::subst; use middle::ty::{ImplContainer, TraitContainer}; use middle::ty::{self, Ty, TyCtxt, TypeFoldable, VariantKind}; +use middle::traits; use rustc_const_eval::ConstInt; @@ -564,6 +565,13 @@ pub fn get_visibility(cdata: Cmd, id: DefIndex) -> hir::Visibility { item_visibility(cdata.lookup_item(id)) } +pub fn get_parent_impl(cdata: Cmd, id: DefIndex) -> Option { + let item = cdata.lookup_item(id); + reader::maybe_get_doc(item, tag_items_data_parent_impl).map(|doc| { + translated_def_id(cdata, doc) + }) +} + pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec { let item = cdata.lookup_item(id); match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index d08cde2df1c04..bbe9d0ea4c609 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -884,6 +884,12 @@ fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option) }); } +fn encode_parent_impl(rbml_w: &mut Encoder, parent_opt: Option) { + parent_opt.map(|parent| { + rbml_w.wr_tagged_u64(tag_items_data_parent_impl, def_to_u64(parent)); + }); +} + fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, xrefs: FnvHashMap, u32>) @@ -1161,8 +1167,12 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, } rbml_w.end_tag(); } - if let Some(trait_ref) = tcx.impl_trait_ref(ecx.tcx.map.local_def_id(item.id)) { + let did = ecx.tcx.map.local_def_id(item.id); + if let Some(trait_ref) = tcx.impl_trait_ref(did) { encode_trait_ref(rbml_w, ecx, trait_ref, tag_item_trait_ref); + + let parent = tcx.lookup_trait_def(trait_ref.def_id).parent_of_impl(did); + encode_parent_impl(rbml_w, parent); } encode_path(rbml_w, path.clone()); encode_stability(rbml_w, stab); diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 9dc8d7ae943a1..d62e7dd43f081 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -15,7 +15,6 @@ // done by the orphan and overlap modules. Then we build up various // mappings. That mapping code resides here. - use middle::def_id::DefId; use middle::lang_items::UnsizeTraitLangItem; use middle::subst::{self, Subst}; @@ -197,7 +196,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { debug!("add_trait_impl: impl_trait_ref={:?} impl_def_id={:?}", impl_trait_ref, impl_def_id); let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id); - trait_def.record_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref); + trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref); } // Converts an implementation in the AST to a vector of items. diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 80430076f197d..47288e33ad615 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -12,7 +12,7 @@ //! same type. Likewise, no two inherent impls for a given type //! constructor provide a method with the same name. -use middle::cstore::{CrateStore, LOCAL_CRATE}; +use middle::cstore::CrateStore; use middle::def_id::DefId; use middle::traits; use middle::ty::{self, TyCtxt}; @@ -50,121 +50,6 @@ struct OverlapChecker<'cx, 'tcx:'cx> { } impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { - fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) { - debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})", - trait_def_id); - - let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id)); - if !self.traits_checked.insert(trait_def_id) { - return; - } - - let trait_def = self.tcx.lookup_trait_def(trait_def_id); - self.tcx.populate_implementations_for_trait_if_necessary( - trait_def.trait_ref.def_id); - - // We should already know all impls of this trait, so these - // borrows are safe. - let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx); - - // Conflicts can only occur between a blanket impl and another impl, - // or between 2 non-blanket impls of the same kind. - - for (i, &impl1_def_id) in blanket_impls.iter().enumerate() { - for &impl2_def_id in &blanket_impls[(i+1)..] { - self.check_if_impls_overlap(impl1_def_id, - impl2_def_id); - } - - for v in nonblanket_impls.values() { - for &impl2_def_id in v { - self.check_if_impls_overlap(impl1_def_id, - impl2_def_id); - } - } - } - - for impl_group in nonblanket_impls.values() { - for (i, &impl1_def_id) in impl_group.iter().enumerate() { - for &impl2_def_id in &impl_group[(i+1)..] { - self.check_if_impls_overlap(impl1_def_id, - impl2_def_id); - } - } - } - } - - // We need to coherently pick which impl will be displayed - // as causing the error message, and it must be the in the current - // crate. Just pick the smaller impl in the file. - fn order_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) - -> Option<(DefId, DefId)> { - if impl1_def_id.krate != LOCAL_CRATE { - if impl2_def_id.krate != LOCAL_CRATE { - // we don't need to check impls if both are external; - // that's the other crate's job. - None - } else { - Some((impl2_def_id, impl1_def_id)) - } - } else if impl2_def_id.krate != LOCAL_CRATE { - Some((impl1_def_id, impl2_def_id)) - } else if impl1_def_id < impl2_def_id { - Some((impl1_def_id, impl2_def_id)) - } else { - Some((impl2_def_id, impl1_def_id)) - } - } - - fn check_if_impls_overlap(&self, - impl1_def_id: DefId, - impl2_def_id: DefId) - { - if let Some((impl1_def_id, impl2_def_id)) = self.order_impls( - impl1_def_id, impl2_def_id) - { - debug!("check_if_impls_overlap({:?}, {:?})", - impl1_def_id, - impl2_def_id); - - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None); - if let Some(header) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) { - self.report_overlap_error(impl1_def_id, impl2_def_id, header.trait_ref.unwrap()); - } - } - } - - fn report_overlap_error(&self, - impl1: DefId, - impl2: DefId, - trait_ref: ty::TraitRef) - { - // only print the Self type if it's concrete; otherwise, it's not adding much information. - let self_type = { - trait_ref.substs.self_ty().and_then(|ty| { - if let ty::TyInfer(_) = ty.sty { - None - } else { - Some(format!(" for type `{}`", ty)) - } - }).unwrap_or(String::new()) - }; - - let mut err = struct_span_err!(self.tcx.sess, self.span_of_def_id(impl1), E0119, - "conflicting implementations of trait `{}`{}:", - trait_ref, - self_type); - - if impl2.is_local() { - span_note!(&mut err, self.span_of_def_id(impl2), - "conflicting implementation is here:"); - } else { - let cname = self.tcx.sess.cstore.crate_name(impl2.krate); - err.note(&format!("conflicting implementation in crate `{}`", cname)); - } - err.emit(); - } - fn span_of_def_id(&self, did: DefId) -> Span { let node_id = self.tcx.map.as_local_node_id(did).unwrap(); self.tcx.map.span(node_id) @@ -222,15 +107,9 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { } } - impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { fn visit_item(&mut self, item: &'v hir::Item) { match item.node { - hir::ItemTrait(..) => { - let trait_def_id = self.tcx.map.local_def_id(item.id); - self.check_for_overlapping_impls_of_trait(trait_def_id); - } - hir::ItemEnum(..) | hir::ItemStruct(..) => { let type_def_id = self.tcx.map.local_def_id(item.id); self.check_for_overlapping_inherent_impls(type_def_id); @@ -246,47 +125,84 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { self.check_for_overlapping_impls_of_trait(trait_ref.def_id); let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id); - match prev_default_impl { - Some(prev_id) => { - self.report_overlap_error(impl_def_id, - self.tcx.map.local_def_id(prev_id), - trait_ref); - } - None => { } + if let Some(prev_id) = prev_default_impl { + span_err!(self.tcx.sess, + self.span_of_def_id(impl_def_id), E0519, + "redundant default implementations of trait `{}`:", + trait_ref); + span_note!(self.tcx.sess, + self.span_of_def_id(self.tcx.map.local_def_id(prev_id)), + "redundant implementation is here:"); } } - hir::ItemImpl(_, _, _, Some(_), _, _) => { + hir::ItemImpl(_, _, _, Some(_), ref self_ty, _) => { let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; - self.check_for_overlapping_impls_of_trait(trait_def_id); - match trait_ref.self_ty().sty { - ty::TyTrait(ref data) => { - // This is something like impl Trait1 for Trait2. Illegal - // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. - if !traits::is_object_safe(self.tcx, data.principal_def_id()) { - // This is an error, but it will be - // reported by wfcheck. Ignore it - // here. This is tested by - // `coherence-impl-trait-for-trait-object-safe.rs`. - } else { - let mut supertrait_def_ids = - traits::supertrait_def_ids(self.tcx, data.principal_def_id()); - if supertrait_def_ids.any(|d| d == trait_def_id) { - span_err!(self.tcx.sess, item.span, E0371, - "the object type `{}` automatically \ - implements the trait `{}`", - trait_ref.self_ty(), - self.tcx.item_path_str(trait_def_id)); + let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id)); + + let def = self.tcx.lookup_trait_def(trait_def_id); + + // attempt to insert into the specialization graph + let insert_result = def.add_impl_for_specialization(self.tcx, + impl_def_id, + trait_ref); + + // insertion failed due to overlap + if let Err(overlap) = insert_result { + // only print the Self type if it has at least some outer + // concrete shell; otherwise, it's not adding much + // information. + let self_type = { + overlap.on_trait_ref.substs.self_ty().and_then(|ty| { + if ty.has_concrete_skeleton() { + Some(format!(" for type `{}`", ty)) + } else { + None } + }).unwrap_or(String::new()) + }; + + span_err!(self.tcx.sess, self.span_of_def_id(impl_def_id), E0119, + "conflicting implementations of trait `{}`{}:", + overlap.on_trait_ref, + self_type); + + if overlap.with_impl.is_local() { + span_note!(self.tcx.sess, self.span_of_def_id(overlap.with_impl), + "conflicting implementation is here:"); + } else { + let cname = self.tcx.sess.cstore.crate_name(overlap.with_impl.krate); + self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", + cname)); + } + } + + // check for overlap with the automatic `impl Trait for Trait` + if let ty::TyTrait(ref data) = trait_ref.self_ty().sty { + // This is something like impl Trait1 for Trait2. Illegal + // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. + + if !traits::is_object_safe(self.tcx, data.principal_def_id()) { + // This is an error, but it will be + // reported by wfcheck. Ignore it + // here. This is tested by + // `coherence-impl-trait-for-trait-object-safe.rs`. + } else { + let mut supertrait_def_ids = + traits::supertrait_def_ids(self.tcx, data.principal_def_id()); + if supertrait_def_ids.any(|d| d == trait_def_id) { + span_err!(self.tcx.sess, item.span, E0371, + "the object type `{}` automatically \ + implements the trait `{}`", + trait_ref.self_ty(), + self.tcx.item_path_str(trait_def_id)); } } - _ => { } } } - _ => { - } + _ => {} } } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 69b84784eb614..4a8ce70ccb926 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -69,6 +69,7 @@ use middle::resolve_lifetime; use middle::const_eval::{self, ConstVal}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace}; +use middle::traits; use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer}; use middle::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeScheme}; use middle::ty::{VariantKind}; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 5c411bec5065d..1a33a5a562a9a 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3695,5 +3695,6 @@ register_diagnostics! { E0399, // trait items need to be implemented because the associated // type `{}` was overridden E0436, // functional record update requires a struct - E0513 // no type for local variable .. + E0513, // no type for local variable .. + E0519 // redundant default implementations of trait } From 957ee5ce347999ef2c1acfdb63a76643c61f6516 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 28 Dec 2015 13:34:01 -0800 Subject: [PATCH 08/37] Add subst helper for inheriting FnSpace from another subst --- src/librustc/middle/subst.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 02dfeb80b928f..9f8e2a22133ef 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -160,6 +160,15 @@ impl<'tcx> Substs<'tcx> { Substs { types: types, regions: regions } } + pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> { + let Substs { types, regions } = self; + let types = types.with_vec(FnSpace, other.types.get_slice(FnSpace).to_vec()); + let regions = regions.map(|r| { + r.with_vec(FnSpace, other.regions().get_slice(FnSpace).to_vec()) + }); + Substs { types: types, regions: regions } + } + /// Creates a trait-ref out of this substs, ignoring the FnSpace substs pub fn to_trait_ref(&self, tcx: &TyCtxt<'tcx>, trait_id: DefId) -> ty::TraitRef<'tcx> { From 7e42a780161e757ddd7d20925691a861f9d86725 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 28 Dec 2015 15:38:26 -0800 Subject: [PATCH 09/37] Implement default method inheritance. This commit leverages the specialization graph infrastructure to allow specializing trait implementations to leave off methods for which their parents have provided defaults. It does not yet check that the `default` keyword is appropriately used in such cases. --- src/librustc/middle/traits/coherence.rs | 5 +- src/librustc/middle/traits/mod.rs | 2 +- src/librustc/middle/traits/select.rs | 2 +- src/librustc/middle/traits/specialize.rs | 254 ++++++++++++++++++++--- src/librustc/middle/ty/mod.rs | 24 --- src/librustc/middle/ty/trait_def.rs | 10 +- src/librustc/middle/ty/util.rs | 55 +---- src/librustc_trans/trans/meth.rs | 43 +++- src/librustc_typeck/check/mod.rs | 39 ++-- 9 files changed, 296 insertions(+), 138 deletions(-) diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 00c72d0b48ab0..33a1e3816e348 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -85,10 +85,7 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>, return None } - let substituted = selcx.infcx().resolve_type_vars_if_possible(&a_impl_header); - let freshened = selcx.infcx().freshen(substituted); - - Some(freshened) + Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header)) } pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index cbb0cc5aed704..8d3403021e415 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -50,7 +50,7 @@ pub use self::select::SelectionContext; pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants -pub use self::specialize::{Overlap, SpecializationGraph, specializes}; +pub use self::specialize::{Overlap, SpecializationGraph, get_impl_item_or_default, ItemSource, specializes}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index a5fe1191d3f0c..d319ac0219c75 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1629,7 +1629,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // i.e. EvaluatedToOk: if other.evaluation == EvaluatedToOk { if let ImplCandidate(victim_def) = victim.candidate { - return traits::specializes(self.infcx(), other_def, victim_def); + return traits::specializes(self.tcx(), other_def, victim_def); } } diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index ad8d6c4f95661..b2339b3080c9a 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -23,7 +23,7 @@ use middle::infer::{self, InferCtxt, TypeOrigin}; use middle::region; use middle::subst::{Subst, Substs}; use middle::traits; -use middle::ty; +use middle::ty::{self, ImplOrTraitItem}; use syntax::codemap::DUMMY_SP; use util::nodemap::DefIdMap; @@ -51,6 +51,8 @@ pub struct SpecializationGraph { /// Information pertinent to an overlapping impl error. pub struct Overlap<'tcx> { pub with_impl: DefId, + + /// NB: this TraitRef can contain inference variables! pub on_trait_ref: ty::TraitRef<'tcx>, } @@ -72,9 +74,7 @@ impl SpecializationGraph { -> Result<(), Overlap<'tcx>> { assert!(impl_def_id.is_local()); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); let mut parent = trait_ref.def_id; - let mut my_children = vec![]; // descend the existing tree, looking for the right location to add this impl @@ -84,13 +84,12 @@ impl SpecializationGraph { for slot in possible_siblings.iter_mut() { let possible_sibling = *slot; - let overlap = infcx.probe(|_| { - traits::overlapping_impls(&infcx, possible_sibling, impl_def_id) - }); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); + let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); if let Some(trait_ref) = overlap { - let le = specializes(&infcx, impl_def_id, possible_sibling); - let ge = specializes(&infcx, possible_sibling, impl_def_id); + let le = specializes(tcx, impl_def_id, possible_sibling); + let ge = specializes(tcx, possible_sibling, impl_def_id); if le && !ge { // the impl specializes possible_sibling @@ -120,22 +119,184 @@ impl SpecializationGraph { } if self.children.insert(impl_def_id, my_children).is_some() { - panic!("When inserting an impl into the specialization graph, existing children for \ - the impl were already present."); + tcx.sess + .bug("When inserting an impl into the specialization graph, existing children for \ + the impl were already present."); } Ok(()) } - /// Insert cached metadata mapping from a child impl back to its parent + /// Insert cached metadata mapping from a child impl back to its parent. pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) { - if self.parent.insert(child, Some(parent)).is_some() { + if self.parent.insert(child, parent).is_some() { panic!("When recording an impl from the crate store, information about its parent \ was already present."); } self.children.entry(parent).or_insert(vec![]).push(child); } + + /// 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() + } +} + +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics (and possibly +/// some relevant `FnSpace` variables as well). And `target_impl` is the impl +/// we're actually going to get the definition from. +fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>, + target_impl: DefId) + -> Substs<'tcx> { + + // We need to build a subst that covers all the generics of + // `target_impl`. Start by introducing fresh infer variables: + let target_generics = tcx.lookup_item_type(target_impl).generics; + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let mut target_substs = infcx.fresh_substs_for_generics(DUMMY_SP, &target_generics); + if source_substs.regions.is_erased() { + target_substs = target_substs.erase_regions() + } + + if !fulfill_implication(&mut infcx, + source_impl, + source_substs.clone(), + target_impl, + target_substs.clone()) { + tcx.sess + .bug("When translating substitutions for specialization, the expected specializaiton \ + failed to hold") + } + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. We + // also carry along any FnSpace substitutions, which don't need to be + // adjusted when mapping from one impl to another. + infcx.resolve_type_vars_if_possible(&target_substs) + .with_method_from_subst(&source_substs) +} + +/// When we've selected an impl but need to use an item definition provided by +/// the trait itself, we need to translate the substitution applied to the impl +/// to one that makes sense for the trait. +fn translate_substs_from_impl_to_trait<'tcx>(tcx: &ty::ctxt<'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>) + -> Substs<'tcx> { + + let source_trait_ref = tcx.impl_trait_ref(source_impl).unwrap().subst(tcx, &source_substs); + + let mut new_substs = source_trait_ref.substs.clone(); + if source_substs.regions.is_erased() { + new_substs = new_substs.erase_regions() + } + + // Carry any FnSpace substitutions along; they don't need to be adjusted + new_substs.with_method_from_subst(&source_substs) +} + +#[derive(Debug, Copy, Clone)] +/// When looking up an item in an impl, it may turn out that the item +/// is actually provided as a default by a more generic impl, or by +/// the trait itself. This enum says where the item came from. +pub enum ItemSource { + Impl { + requested_impl: DefId, + actual_impl: DefId, + }, + Trait { + requested_impl: DefId, + }, +} + +impl ItemSource { + pub fn is_from_trait(&self) -> bool { + match *self { + ItemSource::Trait { .. } => true, + _ => false, + } + } + + /// Given a subst for the requested impl, translate it to a subst + /// appropriate for the actual item definition (whether it be in that impl, + /// a parent impl, or the trait). + pub fn translate_substs<'tcx>(&self, + tcx: &ty::ctxt<'tcx>, + requested_impl_substs: Substs<'tcx>) + -> Substs<'tcx> { + match *self { + ItemSource::Impl { requested_impl, actual_impl } => { + // no need to translate if we're targetting the impl we started with + if requested_impl == actual_impl { + return requested_impl_substs; + } + + translate_substs_between_impls(tcx, + requested_impl, + requested_impl_substs, + actual_impl) + + } + ItemSource::Trait { requested_impl } => { + translate_substs_from_impl_to_trait(tcx, requested_impl, requested_impl_substs) + } + } + } +} + +/// Lookup the definition of an item within `requested_impl` or its specialization +/// parents, including provided items from the trait itself. +/// +/// The closure `f` works in the style of `filter_map`. +pub fn get_impl_item_or_default<'tcx, I, F>(tcx: &ty::ctxt<'tcx>, + requested_impl: DefId, + mut f: F) + -> Option<(I, ItemSource)> + where F: for<'a> FnMut(&ImplOrTraitItem<'tcx>) -> Option +{ + let impl_or_trait_items_map = tcx.impl_or_trait_items.borrow(); + let trait_def_id = tcx.trait_id_of_impl(requested_impl).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + // Walk up the specialization tree, looking for a matching item definition + + let mut current_impl = requested_impl; + loop { + for impl_item_id in &tcx.impl_items.borrow()[¤t_impl] { + let impl_item = &impl_or_trait_items_map[&impl_item_id.def_id()]; + if let Some(t) = f(impl_item) { + let source = ItemSource::Impl { + requested_impl: requested_impl, + actual_impl: current_impl, + }; + return Some((t, source)); + } + } + + if let Some(parent) = trait_def.parent_of_impl(current_impl) { + current_impl = parent; + } else { + break; + } + } + + // The item isn't defined anywhere in the hierarchy. Get the + // default from the trait. + + for trait_item in tcx.trait_items(trait_def_id).iter() { + if let Some(t) = f(trait_item) { + return Some((t, ItemSource::Trait { requested_impl: requested_impl })); + } + } + + None } fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { @@ -155,30 +316,57 @@ fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Sub /// Specialization is determined by the sets of types to which the impls apply; /// impl1 specializes impl2 if it applies to a subset of the types impl2 applies /// to. -pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { - let tcx = &infcx.tcx; - +pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { // We determine whether there's a subset relationship by: // // - skolemizing impl1, + // - instantiating impl2 with fresh inference variables, // - assuming the where clauses for impl1, // - unifying, // - attempting to prove the where clauses for impl2 // + // The last three steps are essentially checking for an implication between two impls + // after appropriate substitutions. This is what `fulfill_implication` checks for. + // // See RFC 1210 for more details and justification. + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id); + let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); + + fulfill_implication(&mut infcx, + impl1_def_id, + impl1_substs, + impl2_def_id, + impl2_substs) +} + +/// Does impl1 (instantiated with the impl1_substs) imply impl2 +/// (instantiated with impl2_substs)? +/// +/// Mutates the `infcx` in two ways: +/// - by adding the obligations of impl1 to the parameter environment +/// - via fulfillment, so that if the implication holds the various unifications +fn fulfill_implication<'a, 'tcx>(infcx: &mut InferCtxt<'a, 'tcx>, + impl1_def_id: DefId, + impl1_substs: Substs<'tcx>, + impl2_def_id: DefId, + impl2_substs: Substs<'tcx>) + -> bool { + let tcx = &infcx.tcx; + let (impl1_trait_ref, impl1_obligations) = { let selcx = &mut SelectionContext::new(&infcx); util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) }; let impl1_predicates: Vec<_> = impl1_obligations.iter() - .cloned() - .map(|oblig| oblig.predicate) - .collect(); + .cloned() + .map(|oblig| oblig.predicate) + .collect(); - let penv = ty::ParameterEnvironment { + infcx.parameter_environment = ty::ParameterEnvironment { tcx: tcx, free_substs: impl1_substs, implicit_region_bound: ty::ReEmpty, // FIXME: is this OK? @@ -188,13 +376,10 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK? }; - // FIXME: unclear what `errors_will_be_reported` should be here... - let infcx = infer::new_infer_ctxt(tcx, infcx.tables, Some(penv), true); let selcx = &mut SelectionContext::new(&infcx); - - let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); - let (impl2_trait_ref, impl2_obligations) = - util::impl_trait_ref_and_oblig(selcx, impl2_def_id, &impl2_substs); + let (impl2_trait_ref, impl2_obligations) = util::impl_trait_ref_and_oblig(selcx, + impl2_def_id, + &impl2_substs); // do the impls unify? If not, no specialization. if let Err(_) = infer::mk_eq_trait_refs(&infcx, @@ -202,7 +387,7 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) TypeOrigin::Misc(DUMMY_SP), impl1_trait_ref, impl2_trait_ref) { - debug!("specializes: {:?} does not unify with {:?}", + debug!("fulfill_implication: {:?} does not unify with {:?}", impl1_trait_ref, impl2_trait_ref); return false; @@ -212,21 +397,24 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) + for oblig in impl2_obligations.into_iter() { fulfill_cx.register_predicate_obligation(&infcx, oblig); } if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { - debug!("specializes: for impls on {:?} and {:?}, could not fulfill: {:?} given {:?}", + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ + {:?}", impl1_trait_ref, impl2_trait_ref, errors, infcx.parameter_environment.caller_bounds); - return false; + false + } else { + debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", + impl1_trait_ref, + impl2_trait_ref); + true } - - debug!("specializes: an impl for {:?} specializes {:?} (`where` clauses elided)", - impl1_trait_ref, - impl2_trait_ref); - true } diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index c336fd558bd56..7fa20f2135ae1 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -2719,28 +2719,4 @@ impl<'tcx> TyCtxt<'tcx> { Some(d) => f(&d[..]) } } - - pub fn make_substs_for_receiver_types(&self, - trait_ref: &ty::TraitRef<'tcx>, - method: &ty::Method<'tcx>) - -> subst::Substs<'tcx> - { - /*! - * Substitutes the values for the receiver's type parameters - * that are found in method, leaving the method's type parameters - * intact. - */ - - let meth_tps: Vec = - method.generics.types.get_slice(subst::FnSpace) - .iter() - .map(|def| self.mk_param_from_def(def)) - .collect(); - let meth_regions: Vec = - method.generics.regions.get_slice(subst::FnSpace) - .iter() - .map(|def| def.to_early_bound_region()) - .collect(); - trait_ref.substs.clone().with_method(meth_tps, meth_regions) - } } diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index a67b2d9fb1cfc..582b289455196 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -190,8 +190,14 @@ impl<'tcx> TraitDef<'tcx> { .insert(tcx, impl_def_id, impl_trait_ref) } - pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { - self.read_trait_impls(tcx); + /// Returns the immediately less specialized impl, if any. + pub fn parent_of_impl(&self, impl_def_id: DefId) -> Option { + let parent = self.specialization_graph.borrow().parent(impl_def_id); + if parent == self.trait_ref.def_id { None } else { Some(parent) } + } + + pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { + self.read_trait_impls(tcx); tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); for &impl_def_id in self.blanket_impls.borrow().iter() { diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index c91441a3f8a4b..db28963d2c0c6 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -14,7 +14,7 @@ use back::svh::Svh; use middle::const_eval::{self, ConstVal, ErrKind}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::def_id::DefId; -use middle::subst::{self, Subst, Substs}; +use middle::subst; use middle::infer; use middle::pat_util; use middle::traits; @@ -26,7 +26,6 @@ use rustc_const_eval::{ConstInt, ConstIsize, ConstUsize}; use std::cmp; use std::hash::{Hash, SipHasher, Hasher}; -use std::rc::Rc; use syntax::ast::{self, Name}; use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt}; use syntax::codemap::Span; @@ -536,58 +535,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[derive(Debug)] -pub struct ImplMethod<'tcx> { - pub method: Rc>, - pub substs: &'tcx Substs<'tcx>, - pub is_provided: bool -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn get_impl_method(&self, - impl_def_id: DefId, - substs: &'tcx Substs<'tcx>, - name: Name) - -> ImplMethod<'tcx> - { - // there don't seem to be nicer accessors to these: - let impl_or_trait_items_map = self.impl_or_trait_items.borrow(); - - for impl_item in &self.impl_items.borrow()[&impl_def_id] { - if let ty::MethodTraitItem(ref meth) = - impl_or_trait_items_map[&impl_item.def_id()] { - if meth.name == name { - return ImplMethod { - method: meth.clone(), - substs: substs, - is_provided: false - } - } - } - } - - // It is not in the impl - get the default from the trait. - let trait_ref = self.impl_trait_ref(impl_def_id).unwrap(); - for trait_item in self.trait_items(trait_ref.def_id).iter() { - if let &ty::MethodTraitItem(ref meth) = trait_item { - if meth.name == name { - let impl_to_trait_substs = self - .make_substs_for_receiver_types(&trait_ref, meth); - let substs = impl_to_trait_substs.subst(self, substs); - return ImplMethod { - method: meth.clone(), - substs: self.mk_substs(substs), - is_provided: true - } - } - } - } - - self.sess.bug(&format!("method {:?} not found in {:?}", - name, impl_def_id)) - } -} - impl<'tcx> ty::TyS<'tcx> { fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>, bound: ty::BuiltinBound, diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 78b86dafa18ad..073ef9797ed2a 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::rc::Rc; + use arena::TypedArena; use back::link; use llvm::{ValueRef, get_params}; @@ -33,7 +35,7 @@ use trans::type_::Type; use trans::type_of::*; use middle::ty::{self, Ty, TyCtxt}; -use syntax::ast; +use syntax::ast::{self, Name}; use syntax::attr; use syntax::codemap::DUMMY_SP; @@ -107,7 +109,7 @@ pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // those from the impl and those from the method: let impl_substs = vtable_impl.substs.with_method_from(&substs); let substs = ccx.tcx().mk_substs(impl_substs); - let mth = ccx.tcx().get_impl_method(impl_did, substs, mname); + let mth = get_impl_method(ccx.tcx(), impl_did, impl_substs, mname); // Translate the function, bypassing Callee::def. // That is because default methods have the same ID as the @@ -428,7 +430,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let mth = tcx.get_impl_method(impl_id, substs, name); + let mth = get_impl_method(tcx, impl_id, substs.clone(), name); debug!("get_vtable_methods: mth={:?}", mth); @@ -466,3 +468,38 @@ fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>) }), }) } + +#[derive(Debug)] +pub struct ImplMethod<'tcx> { + pub method: Rc>, + pub substs: Substs<'tcx>, + pub is_provided: bool +} + +/// Locates the applicable definition of a method, given its name. +pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, + impl_def_id: DefId, + substs: Substs<'tcx>, + name: Name) + -> ImplMethod<'tcx> +{ + assert!(!substs.types.needs_infer()); + + traits::get_impl_item_or_default(tcx, impl_def_id, |cand| { + if let &ty::MethodTraitItem(ref meth) = cand { + if meth.name == name { + return Some(meth.clone()) + } + } + None + }).map(|(meth, source)| { + ImplMethod { + method: meth, + substs: source.translate_substs(tcx, substs), + is_provided: source.is_from_trait(), + } + }).unwrap_or_else(|| { + tcx.sess.bug(&format!("method {:?} not found in {:?}", + name, impl_def_id)) + }) +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19731407d9e7a..136e8c6569b76 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -672,10 +672,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) { hir::ItemFn(..) => {} // entirely within check_item_body hir::ItemImpl(_, _, _, _, _, ref impl_items) => { debug!("ItemImpl {} with id {}", it.name, it.id); - match ccx.tcx.impl_trait_ref(ccx.tcx.map.local_def_id(it.id)) { + let impl_def_id = ccx.tcx.map.local_def_id(it.id); + match ccx.tcx.impl_trait_ref(impl_def_id) { Some(impl_trait_ref) => { check_impl_items_against_trait(ccx, it.span, + impl_def_id, &impl_trait_ref, impl_items); } @@ -864,6 +866,7 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_span: Span, + impl_id: DefId, impl_trait_ref: &ty::TraitRef<'tcx>, impl_items: &[hir::ImplItem]) { // Locate trait methods @@ -973,23 +976,27 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } ty::MethodTraitItem(ref trait_method) => { - let is_implemented = - impl_items.iter().any(|ii| { - match ii.node { - hir::ImplItemKind::Method(..) => { - ii.name == trait_method.name - } - _ => false, + let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| { + if let &ty::MethodTraitItem(ref meth) = cand { + if meth.name == trait_method.name { + return Some(()); } - }); - let is_provided = - provided_methods.iter().any(|m| m.name == trait_method.name); - if !is_implemented { - if !is_provided { - missing_items.push(trait_method.name); - } else if associated_type_overridden { - invalidated_items.push(trait_method.name); } + None + }); + + if let Some((_, source)) = search_result { + if source.is_from_trait() { + let is_provided = + provided_methods.iter().any(|m| m.name == trait_method.name); + if !is_provided { + missing_items.push(trait_method.name); + } else if associated_type_overridden { + invalidated_items.push(trait_method.name); + } + } + } else { + missing_items.push(trait_method.name); } } ty::TypeTraitItem(ref associated_type) => { From 1077ff2deca4c80f27596905119a84564fe9813f Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 28 Dec 2015 15:40:11 -0800 Subject: [PATCH 10/37] Add basic specialization tests, including for default item inheritance. Updates some of the coherence tests as well. --- src/test/auxiliary/go_trait.rs | 4 +- .../auxiliary/specialization_cross_crate.rs | 80 ++++++++++++++ .../specialization_cross_crate_defaults.rs | 49 +++++++++ .../coherence-no-direct-lifetime-dispatch.rs | 18 +++ .../coherence-overlap-messages.rs | 24 ++-- .../specialization-negative-impl.rs | 23 ++++ .../specialization-overlap-negative.rs | 20 ++++ .../compile-fail/specialization-overlap.rs | 23 ++++ .../specialization-allowed-cross-crate.rs | 29 +++++ src/test/run-pass/specialization-assoc-fns.rs | 33 ++++++ src/test/run-pass/specialization-basics.rs | 104 ++++++++++++++++++ .../specialization-cross-crate-defaults.rs | 37 +++++++ .../run-pass/specialization-cross-crate.rs | 56 ++++++++++ .../specialization-default-methods.rs | 66 +++++++++++ .../run-pass/specialization-super-traits.rs | 23 ++++ 15 files changed, 570 insertions(+), 19 deletions(-) create mode 100644 src/test/auxiliary/specialization_cross_crate.rs create mode 100755 src/test/auxiliary/specialization_cross_crate_defaults.rs create mode 100644 src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs create mode 100644 src/test/compile-fail/specialization-negative-impl.rs create mode 100644 src/test/compile-fail/specialization-overlap-negative.rs create mode 100644 src/test/compile-fail/specialization-overlap.rs create mode 100644 src/test/run-pass/specialization-allowed-cross-crate.rs create mode 100644 src/test/run-pass/specialization-assoc-fns.rs create mode 100644 src/test/run-pass/specialization-basics.rs create mode 100644 src/test/run-pass/specialization-cross-crate-defaults.rs create mode 100644 src/test/run-pass/specialization-cross-crate.rs create mode 100644 src/test/run-pass/specialization-default-methods.rs create mode 100644 src/test/run-pass/specialization-super-traits.rs diff --git a/src/test/auxiliary/go_trait.rs b/src/test/auxiliary/go_trait.rs index 0a921c8f5b3a0..ab1051c56501f 100644 --- a/src/test/auxiliary/go_trait.rs +++ b/src/test/auxiliary/go_trait.rs @@ -37,7 +37,7 @@ pub fn go_once(this: G, arg: isize) { impl GoMut for G where G : Go { - fn go_mut(&mut self, arg: isize) { + default fn go_mut(&mut self, arg: isize) { go(&*self, arg) } } @@ -45,7 +45,7 @@ impl GoMut for G impl GoOnce for G where G : GoMut { - fn go_once(mut self, arg: isize) { + default fn go_once(mut self, arg: isize) { go_mut(&mut self, arg) } } diff --git a/src/test/auxiliary/specialization_cross_crate.rs b/src/test/auxiliary/specialization_cross_crate.rs new file mode 100644 index 0000000000000..92c985a3e1dde --- /dev/null +++ b/src/test/auxiliary/specialization_cross_crate.rs @@ -0,0 +1,80 @@ +// 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. + +pub trait Foo { + fn foo(&self) -> &'static str; +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic" + } +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone" + } +} + +impl Foo for (T, U) where T: Clone, U: Clone { + default fn foo(&self) -> &'static str { + "generic pair" + } +} + +impl Foo for (T, T) { + default fn foo(&self) -> &'static str { + "generic uniform pair" + } +} + +impl Foo for (u8, u32) { + default fn foo(&self) -> &'static str { + "(u8, u32)" + } +} + +impl Foo for (u8, u8) { + default fn foo(&self) -> &'static str { + "(u8, u8)" + } +} + +impl Foo for Vec { + default fn foo(&self) -> &'static str { + "generic Vec" + } +} + +impl Foo for Vec { + fn foo(&self) -> &'static str { + "Vec" + } +} + +impl Foo for String { + fn foo(&self) -> &'static str { + "String" + } +} + +impl Foo for i32 { + fn foo(&self) -> &'static str { + "i32" + } +} + +pub trait MyMarker {} +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone + MyMarker" + } +} diff --git a/src/test/auxiliary/specialization_cross_crate_defaults.rs b/src/test/auxiliary/specialization_cross_crate_defaults.rs new file mode 100755 index 0000000000000..b62d80b589fda --- /dev/null +++ b/src/test/auxiliary/specialization_cross_crate_defaults.rs @@ -0,0 +1,49 @@ +// 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)] + +// First, test only use of explicit `default` items: + +pub trait Foo { + fn foo(&self) -> bool; +} + +impl Foo for T { + default fn foo(&self) -> bool { false } +} + +impl Foo for i32 {} + +impl Foo for i64 { + fn foo(&self) -> bool { true } +} + +// Next, test mixture of explicit `default` and provided methods: + +pub trait Bar { + fn bar(&self) -> i32 { 0 } +} + +impl Bar for T {} // use the provided method + +impl Bar for i32 { + fn bar(&self) -> i32 { 1 } +} +impl<'a> Bar for &'a str {} + +impl Bar for Vec { + default fn bar(&self) -> i32 { 2 } +} +impl Bar for Vec {} +impl Bar for Vec { + fn bar(&self) -> i32 { 3 } +} diff --git a/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs b/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs new file mode 100644 index 0000000000000..6de338f1db0fa --- /dev/null +++ b/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs @@ -0,0 +1,18 @@ +// 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. + +// Test that you cannot *directly* dispatch on lifetime requirements + +trait MyTrait {} + +impl MyTrait for T {} +impl MyTrait for T {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/compile-fail/coherence-overlap-messages.rs b/src/test/compile-fail/coherence-overlap-messages.rs index 4f1092f960e0d..2a54ad88055ec 100644 --- a/src/test/compile-fail/coherence-overlap-messages.rs +++ b/src/test/compile-fail/coherence-overlap-messages.rs @@ -15,28 +15,18 @@ impl Foo for U {} trait Bar {} -impl Bar for T {} //~ ERROR conflicting implementations of trait `Bar` for type `u8`: -impl Bar for u8 {} +impl Bar for (T, u8) {} //~ ERROR conflicting implementations of trait `Bar` for type `(u8, u8)`: +impl Bar for (u8, T) {} trait Baz {} -impl Baz for T {} //~ ERROR conflicting implementations of trait `Baz<_>` for type `u8`: +impl Baz for T {} //~ ERROR conflicting implementations of trait `Baz` for type `u8`: impl Baz for u8 {} -trait Quux {} +trait Quux {} -impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_>`: -impl Quux for T {} - -trait Qaar {} - -impl Qaar for T {} //~ ERROR conflicting implementations of trait `Qaar`: -impl Qaar for T {} - -trait Qaax {} - -impl Qaax for T {} -//~^ ERROR conflicting implementations of trait `Qaax` for type `u32`: -impl Qaax for u32 {} +impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`: +impl Quux for T {} +impl Quux for T {} fn main() {} diff --git a/src/test/compile-fail/specialization-negative-impl.rs b/src/test/compile-fail/specialization-negative-impl.rs new file mode 100644 index 0000000000000..d0d698d12e09c --- /dev/null +++ b/src/test/compile-fail/specialization-negative-impl.rs @@ -0,0 +1,23 @@ +// 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(optin_builtin_traits)] + +struct TestType(T); + +unsafe impl Send for TestType {} +impl !Send for TestType {} + +fn assert_send() {} + +fn main() { + assert_send::>(); + assert_send::>(); //~ ERROR +} diff --git a/src/test/compile-fail/specialization-overlap-negative.rs b/src/test/compile-fail/specialization-overlap-negative.rs new file mode 100644 index 0000000000000..cc427b4fed211 --- /dev/null +++ b/src/test/compile-fail/specialization-overlap-negative.rs @@ -0,0 +1,20 @@ +// 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(optin_builtin_traits)] + +trait MyTrait {} + +struct TestType(::std::marker::PhantomData); + +unsafe impl Send for TestType {} +impl !Send for TestType {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/compile-fail/specialization-overlap.rs b/src/test/compile-fail/specialization-overlap.rs new file mode 100644 index 0000000000000..7d14e85fba83d --- /dev/null +++ b/src/test/compile-fail/specialization-overlap.rs @@ -0,0 +1,23 @@ +// 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. + +trait Foo {} +impl Foo for T {} +impl Foo for Vec {} //~ ERROR E0119 + +trait Bar {} +impl Bar for (T, u8) {} +impl Bar for (u8, T) {} //~ ERROR E0119 + +trait Baz {} +impl Baz for u8 {} +impl Baz for T {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/run-pass/specialization-allowed-cross-crate.rs b/src/test/run-pass/specialization-allowed-cross-crate.rs new file mode 100644 index 0000000000000..139c63d3cdb49 --- /dev/null +++ b/src/test/run-pass/specialization-allowed-cross-crate.rs @@ -0,0 +1,29 @@ +// Copyright 2014 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. + +// aux-build:go_trait.rs + +extern crate go_trait; + +use go_trait::{Go,GoMut}; +use std::fmt::Debug; +use std::default::Default; + +struct MyThingy; + +impl Go for MyThingy { + fn go(&self, arg: isize) { } +} + +impl GoMut for MyThingy { + fn go_mut(&mut self, arg: isize) { } +} + +fn main() { } diff --git a/src/test/run-pass/specialization-assoc-fns.rs b/src/test/run-pass/specialization-assoc-fns.rs new file mode 100644 index 0000000000000..750581ffe9234 --- /dev/null +++ b/src/test/run-pass/specialization-assoc-fns.rs @@ -0,0 +1,33 @@ +// 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. + +trait Foo { + fn mk() -> Self; +} + +impl Foo for T { + default fn mk() -> T { + T::default() + } +} + +impl Foo for Vec { + fn mk() -> Vec { + vec![0] + } +} + +fn main() { + let v1: Vec = Foo::mk(); + let v2: Vec = Foo::mk(); + + assert!(v1.len() == 0); + assert!(v2.len() == 1); +} diff --git a/src/test/run-pass/specialization-basics.rs b/src/test/run-pass/specialization-basics.rs new file mode 100644 index 0000000000000..e585c8e235354 --- /dev/null +++ b/src/test/run-pass/specialization-basics.rs @@ -0,0 +1,104 @@ +// Copyright 2014 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. + +// Tests a variety of basic specialization scenarios and method +// dispatch for them. + +trait Foo { + fn foo(&self) -> &'static str; +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic" + } +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone" + } +} + +impl Foo for (T, U) where T: Clone, U: Clone { + default fn foo(&self) -> &'static str { + "generic pair" + } +} + +impl Foo for (T, T) { + default fn foo(&self) -> &'static str { + "generic uniform pair" + } +} + +impl Foo for (u8, u32) { + default fn foo(&self) -> &'static str { + "(u8, u32)" + } +} + +impl Foo for (u8, u8) { + default fn foo(&self) -> &'static str { + "(u8, u8)" + } +} + +impl Foo for Vec { + default fn foo(&self) -> &'static str { + "generic Vec" + } +} + +impl Foo for Vec { + fn foo(&self) -> &'static str { + "Vec" + } +} + +impl Foo for String { + fn foo(&self) -> &'static str { + "String" + } +} + +impl Foo for i32 { + fn foo(&self) -> &'static str { + "i32" + } +} + +struct NotClone; + +trait MyMarker {} +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone + MyMarker" + } +} + +#[derive(Clone)] +struct MarkedAndClone; +impl MyMarker for MarkedAndClone {} + +fn main() { + assert!(NotClone.foo() == "generic"); + assert!(0u8.foo() == "generic Clone"); + assert!(vec![NotClone].foo() == "generic"); + assert!(vec![0u8].foo() == "generic Vec"); + assert!(vec![0i32].foo() == "Vec"); + assert!(0i32.foo() == "i32"); + assert!(String::new().foo() == "String"); + assert!(((), 0).foo() == "generic pair"); + assert!(((), ()).foo() == "generic uniform pair"); + assert!((0u8, 0u32).foo() == "(u8, u32)"); + assert!((0u8, 0u8).foo() == "(u8, u8)"); + assert!(MarkedAndClone.foo() == "generic Clone + MyMarker"); +} diff --git a/src/test/run-pass/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization-cross-crate-defaults.rs new file mode 100644 index 0000000000000..750c3cf8b3ecb --- /dev/null +++ b/src/test/run-pass/specialization-cross-crate-defaults.rs @@ -0,0 +1,37 @@ +// 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. + +// aux-build:specialization_cross_crate_defaults.rs + +#![feature(specialization)] + +extern crate specialization_cross_crate_defaults; + +use specialization_cross_crate_defaults::*; + +fn test_foo() { + assert!(0i8.foo() == false); + assert!(0i32.foo() == false); + assert!(0i64.foo() == true); +} + +fn test_bar() { + assert!(0u8.bar() == 0); + assert!(0i32.bar() == 1); + assert!("hello".bar() == 0); + assert!(vec![()].bar() == 2); + assert!(vec![0i32].bar() == 2); + assert!(vec![0i64].bar() == 3); +} + +fn main() { + test_foo(); + test_bar(); +} diff --git a/src/test/run-pass/specialization-cross-crate.rs b/src/test/run-pass/specialization-cross-crate.rs new file mode 100644 index 0000000000000..c74b1f2e49e43 --- /dev/null +++ b/src/test/run-pass/specialization-cross-crate.rs @@ -0,0 +1,56 @@ +// 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. + +// aux-build:specialization_cross_crate.rs + +extern crate specialization_cross_crate; + +use specialization_cross_crate::*; + +struct NotClone; + +#[derive(Clone)] +struct MarkedAndClone; +impl MyMarker for MarkedAndClone {} + +struct MyType(T); +impl Foo for MyType { + default fn foo(&self) -> &'static str { + "generic MyType" + } +} + +impl Foo for MyType { + fn foo(&self) -> &'static str { + "MyType" + } +} + +struct MyOtherType; +impl Foo for MyOtherType {} + +fn main() { + assert!(NotClone.foo() == "generic"); + assert!(0u8.foo() == "generic Clone"); + assert!(vec![NotClone].foo() == "generic"); + assert!(vec![0u8].foo() == "generic Vec"); + assert!(vec![0i32].foo() == "Vec"); + assert!(0i32.foo() == "i32"); + assert!(String::new().foo() == "String"); + assert!(((), 0).foo() == "generic pair"); + assert!(((), ()).foo() == "generic uniform pair"); + assert!((0u8, 0u32).foo() == "(u8, u32)"); + assert!((0u8, 0u8).foo() == "(u8, u8)"); + assert!(MarkedAndClone.foo() == "generic Clone + MyMarker"); + + assert!(MyType(()).foo() == "generic MyType"); + assert!(MyType(0u8).foo() == "MyType"); + assert!(MyOtherType.foo() == "generic"); +} diff --git a/src/test/run-pass/specialization-default-methods.rs b/src/test/run-pass/specialization-default-methods.rs new file mode 100644 index 0000000000000..7482d3acbc1ac --- /dev/null +++ b/src/test/run-pass/specialization-default-methods.rs @@ -0,0 +1,66 @@ +// 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. + +// First, test only use of explicit `default` items: + +trait Foo { + fn foo(&self) -> bool; +} + +impl Foo for T { + default fn foo(&self) -> bool { false } +} + +impl Foo for i32 {} + +impl Foo for i64 { + fn foo(&self) -> bool { true } +} + +fn test_foo() { + assert!(0i8.foo() == false); + assert!(0i32.foo() == false); + assert!(0i64.foo() == true); +} + +// Next, test mixture of explicit `default` and provided methods: + +trait Bar { + fn bar(&self) -> i32 { 0 } +} + +impl Bar for T {} // use the provided method + +impl Bar for i32 { + fn bar(&self) -> i32 { 1 } +} +impl<'a> Bar for &'a str {} + +impl Bar for Vec { + default fn bar(&self) -> i32 { 2 } +} +impl Bar for Vec {} +impl Bar for Vec { + fn bar(&self) -> i32 { 3 } +} + +fn test_bar() { + assert!(0u8.bar() == 0); + assert!(0i32.bar() == 1); + assert!("hello".bar() == 0); + assert!(vec![()].bar() == 2); + assert!(vec![0i32].bar() == 2); + assert!(vec![0i64].bar() == 3); +} + +fn main() { + test_foo(); + test_bar(); +} diff --git a/src/test/run-pass/specialization-super-traits.rs b/src/test/run-pass/specialization-super-traits.rs new file mode 100644 index 0000000000000..4a30e6bcd25f8 --- /dev/null +++ b/src/test/run-pass/specialization-super-traits.rs @@ -0,0 +1,23 @@ +// Copyright 2014 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. + +// Test that you can specialize via an explicit trait hierarchy + +// FIXME: this doesn't work yet... + +trait Parent {} +trait Child: Parent {} + +trait Foo {} + +impl Foo for T {} +impl Foo for T {} + +fn main() {} From 5dedbdaea4254a78c58c322f636ecd9175cb53fa Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 29 Dec 2015 10:55:34 -0800 Subject: [PATCH 11/37] Check for proper use of `default` keyword in specializing impls. --- src/librustc/middle/traits/mod.rs | 3 +- src/librustc/middle/traits/specialize.rs | 20 +++++++ src/librustc/middle/ty/mod.rs | 11 +++- src/librustc_typeck/check/mod.rs | 56 ++++++++++++++++++- src/librustc_typeck/coherence/overlap.rs | 17 +++--- src/librustc_typeck/diagnostics.rs | 3 +- .../compile-fail/specialization-no-default.rs | 41 ++++++++++++++ 7 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 src/test/compile-fail/specialization-no-default.rs diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 8d3403021e415..180a1312e2500 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -50,7 +50,8 @@ pub use self::select::SelectionContext; pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants -pub use self::specialize::{Overlap, SpecializationGraph, get_impl_item_or_default, ItemSource, specializes}; +pub use self::specialize::{Overlap, SpecializationGraph, specializes}; +pub use self::specialize::{ItemSource, get_impl_item_or_default, get_parent_impl_item}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index b2339b3080c9a..39cbd36260db5 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -299,6 +299,26 @@ pub fn get_impl_item_or_default<'tcx, I, F>(tcx: &ty::ctxt<'tcx>, None } +/// Convenience function for locating an item defined in a specialization parent, if any. +pub fn get_parent_impl_item<'tcx, I, F>(tcx: &ty::ctxt<'tcx>, + child_impl: DefId, + f: F) + -> Option<(I, DefId)> + where F: for<'a> FnMut(&ImplOrTraitItem<'tcx>) -> Option +{ + let trait_def_id = tcx.trait_id_of_impl(child_impl).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + trait_def.parent_of_impl(child_impl) + .and_then(|parent_impl| get_impl_item_or_default(tcx, parent_impl, f)) + .and_then(|(item, source)| { + match source { + ItemSource::Trait { .. } => None, + ItemSource::Impl { actual_impl, .. } => Some((item, actual_impl)), + } + }) +} + fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { let impl_generics = tcx.lookup_item_type(impl_def_id).generics; diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 7fa20f2135ae1..081196835936c 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -2670,7 +2670,6 @@ impl<'tcx> TyCtxt<'tcx> { Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone()) } - pub fn visit_all_items_in_krate(&self, dep_node_fn: F, visitor: &mut V) @@ -2678,6 +2677,16 @@ impl<'tcx> TyCtxt<'tcx> { { dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor); } + /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` + /// with the name of the crate containing the impl. + pub fn span_of_impl(&self, impl_did: DefId) -> Result { + if impl_did.is_local() { + let node_id = self.map.as_local_node_id(impl_did).unwrap(); + Ok(self.map.span(node_id)) + } else { + Err(self.sess.cstore.crate_name(impl_did.krate)) + } + } } /// The category of explicit self. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 136e8c6569b76..b6a1337dce052 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -127,7 +127,7 @@ use syntax::util::lev_distance::find_best_match_for_name; use rustc_front::intravisit::{self, Visitor}; use rustc_front::hir; -use rustc_front::hir::{Visibility, PatKind}; +use rustc_front::hir::{Visibility, PatKind, Defaultness}; use rustc_front::print::pprust; use rustc_back::slice; @@ -864,6 +864,33 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env); } +fn check_specialization_validity<'tcx, F>(tcx: &ty::ctxt<'tcx>, + impl_id: DefId, + impl_item: &hir::ImplItem, + f: F) + where F: FnMut(&ty::ImplOrTraitItem<'tcx>) -> Option +{ + let parent_item_opt = traits::get_parent_impl_item(tcx, impl_id, f); + if let Some((Defaultness::Final, parent_impl)) = parent_item_opt { + span_err!(tcx.sess, impl_item.span, E0520, + "item `{}` is provided by an implementation that \ + specializes another, but the item in the parent \ + implementations is not marked `default` and so it \ + cannot be specialized.", + impl_item.name); + + match tcx.span_of_impl(parent_impl) { + Ok(span) => { + span_note!(tcx.sess, span, "parent implementation is here:"); + } + Err(cname) => { + tcx.sess.note(&format!("parent implementation is in crate `{}`", + cname)); + } + } + } +} + fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_span: Span, impl_id: DefId, @@ -903,6 +930,15 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_const.name, impl_trait_ref) } + + check_specialization_validity(ccx.tcx, impl_id, impl_item, |cand| { + if let &ty::ConstTraitItem(ref trait_const) = cand { + if trait_const.name == impl_item.name { + return Some(trait_const.defaultness); + } + } + None + }); } hir::ImplItemKind::Method(ref sig, ref body) => { check_trait_fn_not_const(ccx, impl_item.span, sig.constness); @@ -926,6 +962,15 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_method.name, impl_trait_ref) } + + check_specialization_validity(ccx.tcx, impl_id, impl_item, |cand| { + if let &ty::MethodTraitItem(ref meth) = cand { + if meth.name == impl_method.name { + return Some(meth.defaultness); + } + } + None + }); } hir::ImplItemKind::Type(_) => { let impl_type = match ty_impl_item { @@ -944,6 +989,15 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_type.name, impl_trait_ref) } + + check_specialization_validity(ccx.tcx, impl_id, impl_item, |cand| { + if let &ty::TypeTraitItem(ref at) = cand { + if at.name == impl_item.name { + return Some(at.defaultness); + } + } + None + }); } } } diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 47288e33ad615..97cdcd4ba34e5 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -13,12 +13,10 @@ //! constructor provide a method with the same name. use middle::cstore::CrateStore; -use middle::def_id::DefId; use middle::traits; use middle::ty::{self, TyCtxt}; use middle::infer; use syntax::ast; -use syntax::codemap::Span; use rustc::dep_graph::DepNode; use rustc_front::hir; use rustc_front::intravisit; @@ -169,13 +167,14 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { overlap.on_trait_ref, self_type); - if overlap.with_impl.is_local() { - span_note!(self.tcx.sess, self.span_of_def_id(overlap.with_impl), - "conflicting implementation is here:"); - } else { - let cname = self.tcx.sess.cstore.crate_name(overlap.with_impl.krate); - self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", - cname)); + match self.tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + span_note!(self.tcx.sess, span, "conflicting implementation is here:"); + } + Err(cname) => { + self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", + cname)); + } } } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 1a33a5a562a9a..79508c2ca9f8d 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3696,5 +3696,6 @@ register_diagnostics! { // type `{}` was overridden E0436, // functional record update requires a struct E0513, // no type for local variable .. - E0519 // redundant default implementations of trait + E0519, // redundant default implementations of trait + E0520 // cannot specialize non-default item } diff --git a/src/test/compile-fail/specialization-no-default.rs b/src/test/compile-fail/specialization-no-default.rs new file mode 100644 index 0000000000000..3e23c6e06eafb --- /dev/null +++ b/src/test/compile-fail/specialization-no-default.rs @@ -0,0 +1,41 @@ +// 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. + +trait Foo { + fn foo(&self); + fn bar(&self); +} + +impl Foo for T { + fn foo(&self) {} + fn bar(&self) {} +} + +impl Foo for u8 {} +impl Foo for u16 { + fn foo(&self) {} //~ ERROR E0520 +} +impl Foo for u32 { + fn bar(&self) {} //~ ERROR E0520 +} + +trait Bar { + type T; +} + +impl Bar for T { + type T = u8; +} + +impl Bar for u8 { + type T = (); //~ ERROR E0520 +} + +fn main() {} From b7e5112e88b8e73a5bff5a84f37f1d2a608e821c Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 29 Dec 2015 13:37:34 -0800 Subject: [PATCH 12/37] Implement default associated type inheritance. This commit leverages the specialization graph infrastructure to allow specializing trait implementations to leave off associated types for which their parents have provided defaults. It also modifies the type projection code to avoid projecting associated types unless either (1) all input types are fully known or (2) the available associated type is "final", i.e. not marked `default`. This restriction is required for soundness, due to examples like: ```rust trait Foo { type Assoc; } impl Foo for T { default type Assoc = (); } impl Foo for u8 { type Assoc = String; } fn generic() -> ::Assoc { () //~ ERROR } fn main() { let s: String = generic::(); println!("{}", s); // bad news } ``` --- src/librustc/middle/traits/project.rs | 74 ++++++++++++++++----------- src/librustc_typeck/check/mod.rs | 24 ++++----- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index e36307feddbf7..dc279aae32cbb 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -11,6 +11,7 @@ //! Code for projecting associated types out of trait references. use super::elaborate_predicates; +use super::get_impl_item_or_default; use super::report_overflow_error; use super::Obligation; use super::ObligationCause; @@ -23,8 +24,9 @@ use super::util; use middle::infer::{self, TypeOrigin}; use middle::subst::Subst; -use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt}; +use middle::ty::{self, ToPredicate, RegionEscape, HasTypeFlags, ToPolyTraitRef, Ty, TyCtxt}; use middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_front::hir; use syntax::parse::token; use util::common::FN_OUTPUT_NAME; @@ -742,6 +744,28 @@ fn assemble_candidates_from_impls<'cx,'tcx>( match vtable { super::VtableImpl(data) => { + if data.substs.types.needs_infer() { + let assoc_ty_opt = get_impl_item_or_default(selcx.tcx(), data.impl_def_id, |cand| { + if let &ty::TypeTraitItem(ref assoc_ty) = cand { + if assoc_ty.name == obligation.predicate.item_name { + return Some(assoc_ty.defaultness); + } + } + None + }); + + if let Some((defaultness, source)) = assoc_ty_opt { + if !source.is_from_trait() && defaultness == hir::Defaultness::Default { + // FIXME: is it OK to not mark as ambiguous? + return Ok(()); + } + } else { + selcx.tcx().sess.span_bug(obligation.cause.span, + &format!("No associated type for {:?}", + obligation_trait_ref)); + } + } + debug!("assemble_candidates_from_impls: impl candidate {:?}", data); @@ -941,43 +965,31 @@ fn confirm_impl_candidate<'cx,'tcx>( impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>) -> (Ty<'tcx>, Vec>) { - // there don't seem to be nicer accessors to these: - let impl_or_trait_items_map = selcx.tcx().impl_or_trait_items.borrow(); - - // Look for the associated type in the impl - for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] { - if let ty::TypeTraitItem(ref assoc_ty) = impl_or_trait_items_map[&impl_item.def_id()] { - if assoc_ty.name == obligation.predicate.item_name { - return (assoc_ty.ty.unwrap().subst(selcx.tcx(), impl_vtable.substs), - impl_vtable.nested); - } - } - } + let VtableImplData { substs, nested, impl_def_id } = impl_vtable; - // It is not in the impl - get the default from the trait. - let trait_ref = obligation.predicate.trait_ref; - for trait_item in selcx.tcx().trait_items(trait_ref.def_id).iter() { - if let &ty::TypeTraitItem(ref assoc_ty) = trait_item { + get_impl_item_or_default(selcx.tcx(), impl_def_id, |cand| { + if let &ty::TypeTraitItem(ref assoc_ty) = cand { if assoc_ty.name == obligation.predicate.item_name { if let Some(ty) = assoc_ty.ty { - return (ty.subst(selcx.tcx(), trait_ref.substs), - impl_vtable.nested); + return Some(ty) } else { - // This means that the impl is missing a - // definition for the associated type. This error - // ought to be reported by the type checker method - // `check_impl_items_against_trait`, so here we - // just return TyError. + // This means that the impl is missing a definition for the + // associated type. This error will be reported by the type + // checker method `check_impl_items_against_trait`, so here + // we just return TyError. debug!("confirm_impl_candidate: no associated type {:?} for {:?}", assoc_ty.name, - trait_ref); - return (selcx.tcx().types.err, vec!()); + obligation.predicate.trait_ref); + return Some(selcx.tcx().types.err); } } } - } - - selcx.tcx().sess.span_bug(obligation.cause.span, - &format!("No associated type for {:?}", - trait_ref)); + None + }).map(|(ty, source)| { + (ty.subst(selcx.tcx(), &source.translate_substs(selcx.tcx(), substs)), nested) + }).unwrap_or_else(|| { + selcx.tcx().sess.span_bug(obligation.cause.span, + &format!("No associated type for {:?}", + obligation.predicate.trait_ref)); + }) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b6a1337dce052..be2f63d1d1bc9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1053,22 +1053,22 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, missing_items.push(trait_method.name); } } - ty::TypeTraitItem(ref associated_type) => { - let is_implemented = impl_items.iter().any(|ii| { - match ii.node { - hir::ImplItemKind::Type(_) => { - ii.name == associated_type.name + ty::TypeTraitItem(ref trait_assoc_ty) => { + let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| { + if let &ty::TypeTraitItem(ref assoc_ty) = cand { + if assoc_ty.name == trait_assoc_ty.name && assoc_ty.ty.is_some() { + return Some(()); } - _ => false, } + None }); - let is_provided = associated_type.ty.is_some(); - if !is_implemented { - if !is_provided { - missing_items.push(associated_type.name); - } else if associated_type_overridden { - invalidated_items.push(associated_type.name); + + if let Some((_, source)) = search_result { + if source.is_from_trait() && associated_type_overridden { + invalidated_items.push(trait_assoc_ty.name); } + } else { + missing_items.push(trait_assoc_ty.name); } } } From a3825827b7a91a3768959def9ce80a442ed6c76a Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 29 Dec 2015 14:01:30 -0800 Subject: [PATCH 13/37] Add tests for specialization on associated types --- .../specialization-default-projection.rs | 32 ++++++++++++++ .../specialization-default-types.rs | 43 +++++++++++++++++++ .../run-pass/specialization-on-projection.rs | 27 ++++++++++++ .../run-pass/specialization-projection.rs | 27 ++++++++++++ ...ation-translate-projections-with-params.rs | 34 +++++++++++++++ .../specialization-translate-projections.rs | 36 ++++++++++++++++ 6 files changed, 199 insertions(+) create mode 100644 src/test/compile-fail/specialization-default-projection.rs create mode 100644 src/test/compile-fail/specialization-default-types.rs create mode 100644 src/test/run-pass/specialization-on-projection.rs create mode 100644 src/test/run-pass/specialization-projection.rs create mode 100644 src/test/run-pass/specialization-translate-projections-with-params.rs create mode 100644 src/test/run-pass/specialization-translate-projections.rs diff --git a/src/test/compile-fail/specialization-default-projection.rs b/src/test/compile-fail/specialization-default-projection.rs new file mode 100644 index 0000000000000..b85dba126e613 --- /dev/null +++ b/src/test/compile-fail/specialization-default-projection.rs @@ -0,0 +1,32 @@ +// 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. + +// Make sure we can't project defaulted associated types + +trait Foo { + type Assoc; +} + +impl Foo for T { + default type Assoc = (); +} + +impl Foo for u8 { + type Assoc = String; +} + +fn generic() -> ::Assoc { + () //~ ERROR +} + +fn main() { + let s: String = generic::(); + println!("{}", s); // bad news +} diff --git a/src/test/compile-fail/specialization-default-types.rs b/src/test/compile-fail/specialization-default-types.rs new file mode 100644 index 0000000000000..f6b2dd46efc29 --- /dev/null +++ b/src/test/compile-fail/specialization-default-types.rs @@ -0,0 +1,43 @@ +// 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. + +// It should not be possible to use the concrete value of a defaulted +// associated type in the impl defining it -- otherwise, what happens +// if it's overridden? + +trait Example { + type Output; + fn generate(self) -> Self::Output; +} + +impl Example for T { + default type Output = Box; + default fn generate(self) -> Self::Output { + Box::new(self) //~ ERROR + } +} + +impl Example for bool { + type Output = bool; + fn generate(self) -> bool { self } +} + +fn trouble(t: T) -> Box { + Example::generate(t) //~ ERROR +} + +fn weaponize() -> bool { + let b: Box = trouble(true); + *b +} + +fn main() { + weaponize(); +} diff --git a/src/test/run-pass/specialization-on-projection.rs b/src/test/run-pass/specialization-on-projection.rs new file mode 100644 index 0000000000000..f206153266222 --- /dev/null +++ b/src/test/run-pass/specialization-on-projection.rs @@ -0,0 +1,27 @@ +// 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. + +trait Foo {} + +trait Assoc { + type Item; +} + +impl Foo for T {} + +struct Struct; + +impl Assoc for Struct { + type Item = u8; +} + +impl Foo for Struct {} + +fn main() {} diff --git a/src/test/run-pass/specialization-projection.rs b/src/test/run-pass/specialization-projection.rs new file mode 100644 index 0000000000000..86fcab1fdc284 --- /dev/null +++ b/src/test/run-pass/specialization-projection.rs @@ -0,0 +1,27 @@ +// 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. + +// Make sure we *can* project non-defaulted associated types +// cf compile-fail/specialization-default-projection.rs + +trait Foo { + type Assoc; +} + +impl Foo for T { + type Assoc = (); +} + +fn generic() -> ::Assoc { + () +} + +fn main() { +} diff --git a/src/test/run-pass/specialization-translate-projections-with-params.rs b/src/test/run-pass/specialization-translate-projections-with-params.rs new file mode 100644 index 0000000000000..6bca8939d82a9 --- /dev/null +++ b/src/test/run-pass/specialization-translate-projections-with-params.rs @@ -0,0 +1,34 @@ +// 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. + +trait Trait { + fn convert(&self) -> T; +} +trait WithAssoc { + type Item; + fn as_item(&self) -> &Self::Item; +} + +impl Trait for T where T: WithAssoc, U: Clone { + fn convert(&self) -> U { + self.as_item().clone() + } +} + +impl WithAssoc for u8 { + type Item = u8; + fn as_item(&self) -> &u8 { self } +} + +impl Trait for u8 {} + +fn main() { + assert!(3u8.convert() == 3u8); +} diff --git a/src/test/run-pass/specialization-translate-projections.rs b/src/test/run-pass/specialization-translate-projections.rs new file mode 100644 index 0000000000000..7f41729a527da --- /dev/null +++ b/src/test/run-pass/specialization-translate-projections.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. + +use std::convert::Into; + +trait Trait { + fn to_u8(&self) -> u8; +} +trait WithAssoc { + type Item; + fn to_item(&self) -> Self::Item; +} + +impl Trait for T where T: WithAssoc, U: Into { + fn to_u8(&self) -> u8 { + self.to_item().into() + } +} + +impl WithAssoc for u8 { + type Item = u8; + fn to_item(&self) -> u8 { *self } +} + +impl Trait for u8 {} + +fn main() { + assert!(3u8.to_u8() == 3u8); +} From d8160799b5c464ccdb99e4bab7196440106c1972 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 29 Dec 2015 21:18:24 -0800 Subject: [PATCH 14/37] Adjust overlap-related tests to account for cosmetic changes to error reporting behavior --- .../associated-types-coherence-failure.rs | 10 +++++----- ...anket-conflicts-with-blanket-implemented.rs | 4 ++-- ...ket-conflicts-with-blanket-unimplemented.rs | 4 ++-- ...et-conflicts-with-specific-multidispatch.rs | 4 ++-- ...ce-blanket-conflicts-with-specific-trait.rs | 4 ++-- ...oherence-blanket-conflicts-with-specific.rs | 4 ++-- ...oherence-conflicting-negative-trait-impl.rs | 2 -- .../coherence-overlap-all-t-and-tuple.rs | 4 ++-- .../coherence-overlap-issue-23516.rs | 4 ++-- .../compile-fail/coherence-overlap-messages.rs | 18 +++++++++--------- .../compile-fail/coherence-tuple-conflict.rs | 4 ++-- ...e_copy_like_err_fundamental_struct_tuple.rs | 4 ++-- .../coherence_copy_like_err_struct.rs | 4 ++-- .../coherence_copy_like_err_tuple.rs | 4 ++-- src/test/compile-fail/issue-28568.rs | 4 ++-- 15 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/test/compile-fail/associated-types-coherence-failure.rs b/src/test/compile-fail/associated-types-coherence-failure.rs index 6d68da54112f2..786a25500a886 100644 --- a/src/test/compile-fail/associated-types-coherence-failure.rs +++ b/src/test/compile-fail/associated-types-coherence-failure.rs @@ -22,21 +22,21 @@ pub trait IntoCow<'a, B: ?Sized> { fn into_cow(self) -> Cow<'a, B>; } -impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned { -//~^ ERROR E0119 +impl<'a, B: ?Sized> IntoCow<'a, B> for ::Owned where B: ToOwned { fn into_cow(self) -> Cow<'a, B> { - self + Cow(PhantomData) } } -impl<'a, B: ?Sized> IntoCow<'a, B> for ::Owned where B: ToOwned { +impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned { //~^ ERROR E0119 fn into_cow(self) -> Cow<'a, B> { - Cow(PhantomData) + self } } impl<'a, B: ?Sized> IntoCow<'a, B> for &'a B where B: ToOwned { +//~^ ERROR E0119 fn into_cow(self) -> Cow<'a, B> { Cow(PhantomData) } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs index b771b959d3e50..434d77828b44a 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs @@ -27,11 +27,11 @@ impl Even for isize { } impl Odd for usize { } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } -impl MyTrait for T { +impl MyTrait for T { //~ ERROR E0119 fn get(&self) -> usize { 0 } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs index d3b0e7f10b91b..7ad5cd71ca8c2 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs @@ -23,11 +23,11 @@ trait Even {} trait Odd {} -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } -impl MyTrait for T { +impl MyTrait for T { //~ ERROR E0119 fn get(&self) -> usize { 0 } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs index 7b60a5ecbd71f..1defe6c8b20e3 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs @@ -18,7 +18,7 @@ trait MyTrait { fn get(&self) -> T; } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> T { panic!() } @@ -29,7 +29,7 @@ struct MyType { dummy: usize } -impl MyTrait for MyType { +impl MyTrait for MyType { //~ ERROR E0119 fn get(&self) -> usize { (*self).clone() } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs index eeaa68677eb67..5c5c4d32d675c 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs @@ -19,7 +19,7 @@ trait MyTrait { fn get(&self) -> usize; } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } @@ -27,7 +27,7 @@ struct MyType { dummy: usize } -impl MyTrait for MyType { +impl MyTrait for MyType { //~ ERROR E0119 fn get(&self) -> usize { self.dummy } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs index d218b64af0527..57d71b44b0f6b 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs @@ -18,7 +18,7 @@ trait MyTrait { fn get(&self) -> usize; } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } @@ -26,7 +26,7 @@ struct MyType { dummy: usize } -impl MyTrait for MyType { +impl MyTrait for MyType { //~ ERROR E0119 fn get(&self) -> usize { self.dummy } } 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 344ec89d25de9..c123e381ab7d7 100644 --- a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs +++ b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs @@ -15,8 +15,6 @@ trait MyTrait {} struct TestType(::std::marker::PhantomData); unsafe impl Send for TestType {} -//~^ ERROR conflicting implementations of trait `core::marker::Send` -//~^^ ERROR conflicting implementations of trait `core::marker::Send` impl !Send for TestType {} //~^ ERROR conflicting implementations of trait `core::marker::Send` diff --git a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs index 3fd635b3d616f..928ba7a36db26 100644 --- a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs +++ b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs @@ -19,10 +19,10 @@ trait From { } -impl From for T { //~ ERROR E0119 +impl From for T { } -impl From<(U11,)> for (T11,) { +impl From<(U11,)> for (T11,) { //~ ERROR E0119 } fn main() { } diff --git a/src/test/compile-fail/coherence-overlap-issue-23516.rs b/src/test/compile-fail/coherence-overlap-issue-23516.rs index d7f060a3bfe73..51d7c3e8b4cb1 100644 --- a/src/test/compile-fail/coherence-overlap-issue-23516.rs +++ b/src/test/compile-fail/coherence-overlap-issue-23516.rs @@ -14,6 +14,6 @@ pub trait Sugar { fn dummy(&self) { } } pub trait Sweet { fn dummy(&self) { } } -impl Sweet for T { } //~ ERROR E0119 -impl Sweet for Box { } +impl Sweet for T { } +impl Sweet for Box { } //~ ERROR E0119 fn main() { } diff --git a/src/test/compile-fail/coherence-overlap-messages.rs b/src/test/compile-fail/coherence-overlap-messages.rs index 2a54ad88055ec..0ae8135221c21 100644 --- a/src/test/compile-fail/coherence-overlap-messages.rs +++ b/src/test/compile-fail/coherence-overlap-messages.rs @@ -10,23 +10,23 @@ trait Foo {} -impl Foo for T {} //~ ERROR conflicting implementations of trait `Foo`: -impl Foo for U {} +impl Foo for T {} +impl Foo for U {} //~ ERROR conflicting implementations of trait `Foo`: trait Bar {} -impl Bar for (T, u8) {} //~ ERROR conflicting implementations of trait `Bar` for type `(u8, u8)`: -impl Bar for (u8, T) {} +impl Bar for (T, u8) {} +impl Bar for (u8, T) {} //~ ERROR conflicting implementations of trait `Bar` for type `(u8, u8)`: trait Baz {} -impl Baz for T {} //~ ERROR conflicting implementations of trait `Baz` for type `u8`: -impl Baz for u8 {} +impl Baz for T {} +impl Baz for u8 {} //~ ERROR conflicting implementations of trait `Baz` for type `u8`: trait Quux {} -impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`: -impl Quux for T {} -impl Quux for T {} +impl Quux for T {} +impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`: +impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`: fn main() {} diff --git a/src/test/compile-fail/coherence-tuple-conflict.rs b/src/test/compile-fail/coherence-tuple-conflict.rs index 87b007fdd6982..7807f93df1a67 100644 --- a/src/test/compile-fail/coherence-tuple-conflict.rs +++ b/src/test/compile-fail/coherence-tuple-conflict.rs @@ -18,11 +18,11 @@ trait MyTrait { fn get(&self) -> usize; } -impl MyTrait for (T,T) { //~ ERROR E0119 +impl MyTrait for (T,T) { fn get(&self) -> usize { 0 } } -impl MyTrait for (A,B) { +impl MyTrait for (A,B) { //~ ERROR E0119 fn get(&self) -> usize { self.dummy } } diff --git a/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs b/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs index a6b62d17bc4e6..8e3e3f31cb5f1 100644 --- a/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs +++ b/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs @@ -21,10 +21,10 @@ struct MyType { x: i32 } trait MyTrait { } -impl MyTrait for T { } //~ ERROR E0119 +impl MyTrait for T { } // Tuples are not fundamental. -impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { } +impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { } //~ ERROR E0119 #[rustc_error] fn main() { } diff --git a/src/test/compile-fail/coherence_copy_like_err_struct.rs b/src/test/compile-fail/coherence_copy_like_err_struct.rs index 5a9f440f8bb6a..35bc17b8e8870 100644 --- a/src/test/compile-fail/coherence_copy_like_err_struct.rs +++ b/src/test/compile-fail/coherence_copy_like_err_struct.rs @@ -18,7 +18,7 @@ extern crate coherence_copy_like_lib as lib; struct MyType { x: i32 } trait MyTrait { } -impl MyTrait for T { } //~ ERROR E0119 +impl MyTrait for T { } // `MyStruct` is not declared fundamental, therefore this would // require that @@ -26,6 +26,6 @@ impl MyTrait for T { } //~ ERROR E0119 // MyStruct: !MyTrait // // which we cannot approve. -impl MyTrait for lib::MyStruct { } +impl MyTrait for lib::MyStruct { } //~ ERROR E0119 fn main() { } diff --git a/src/test/compile-fail/coherence_copy_like_err_tuple.rs b/src/test/compile-fail/coherence_copy_like_err_tuple.rs index ee0d5550fd61f..a70cc92955fb0 100644 --- a/src/test/compile-fail/coherence_copy_like_err_tuple.rs +++ b/src/test/compile-fail/coherence_copy_like_err_tuple.rs @@ -18,13 +18,13 @@ extern crate coherence_copy_like_lib as lib; struct MyType { x: i32 } trait MyTrait { } -impl MyTrait for T { } //~ ERROR E0119 +impl MyTrait for T { } // Tuples are not fundamental, therefore this would require that // // (MyType,): !MyTrait // // which we cannot approve. -impl MyTrait for (MyType,) { } +impl MyTrait for (MyType,) { } //~ ERROR E0119 fn main() { } diff --git a/src/test/compile-fail/issue-28568.rs b/src/test/compile-fail/issue-28568.rs index 1dfff144cef98..7c051784f61a7 100644 --- a/src/test/compile-fail/issue-28568.rs +++ b/src/test/compile-fail/issue-28568.rs @@ -11,12 +11,12 @@ struct MyStruct; impl Drop for MyStruct { -//~^ ERROR conflicting implementations of trait +//~^ NOTE conflicting implementation is here fn drop(&mut self) { } } impl Drop for MyStruct { -//~^ NOTE conflicting implementation is here +//~^ ERROR conflicting implementations of trait fn drop(&mut self) { } } From 7976e3654400a53fc9ef98cda19769fabd57b846 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 30 Dec 2015 06:56:06 -0800 Subject: [PATCH 15/37] Adjust test for new overlap message on default trait impls --- src/test/compile-fail/coherence-default-trait-impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/coherence-default-trait-impl.rs b/src/test/compile-fail/coherence-default-trait-impl.rs index 0705702b031ee..3d109de76ccd1 100644 --- a/src/test/compile-fail/coherence-default-trait-impl.rs +++ b/src/test/compile-fail/coherence-default-trait-impl.rs @@ -15,7 +15,7 @@ trait MyTrait {} impl MyTrait for .. {} impl MyTrait for .. {} -//~^ ERROR conflicting implementations of trait `MyTrait` +//~^ ERROR redundant default implementations of trait `MyTrait` trait MySafeTrait {} From 9f16c2ce59ac942e1c0415987012100756a44039 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 30 Dec 2015 07:00:05 -0800 Subject: [PATCH 16/37] Adjust coherence test to reflect that only the orphan rule prevents you from adding *generalizing* impls --- src/test/compile-fail/coherence-cross-crate-conflict.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/compile-fail/coherence-cross-crate-conflict.rs b/src/test/compile-fail/coherence-cross-crate-conflict.rs index a020b518d8273..9f74afbb2b3b5 100644 --- a/src/test/compile-fail/coherence-cross-crate-conflict.rs +++ b/src/test/compile-fail/coherence-cross-crate-conflict.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Regression test for #3512 - conflicting trait impls in different crates should give a -// 'conflicting implementations' error message. +// The error here is strictly due to orphan rules; the impl here +// generalizes the one upstream // aux-build:trait_impl_conflict.rs extern crate trait_impl_conflict; @@ -17,7 +17,6 @@ use trait_impl_conflict::Foo; impl Foo for A { //~^ ERROR type parameter `A` must be used as the type parameter for some local type - //~^^ ERROR E0119 } fn main() { From e81691039854e678381042ce93e13bee6971c3d9 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 30 Dec 2015 15:16:43 -0800 Subject: [PATCH 17/37] Add feature gate --- src/librustc/middle/traits/specialize.rs | 10 ++++++++++ src/libsyntax/feature_gate.rs | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index 39cbd36260db5..0f47e19eaf3f4 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -88,6 +88,16 @@ impl SpecializationGraph { let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); if let Some(trait_ref) = overlap { + if !tcx.sess.features.borrow().specialization { + // if specialization is not turned on, all overlaps + // should immediately trigger an error + + return Err(Overlap { + with_impl: possible_sibling, + on_trait_ref: trait_ref, + }); + } + let le = specializes(tcx, impl_def_id, possible_sibling); let ge = specializes(tcx, possible_sibling, impl_def_id); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 14a3f93738a32..d50eb17c87b83 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -248,6 +248,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // `expr?` ("question_mark", "1.9.0", Some(31436), Active) + + // impl specialization (RFC 1210) + ("specialization", "1.7.0", None, Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -574,6 +577,7 @@ pub struct Features { pub stmt_expr_attributes: bool, pub deprecated: bool, pub question_mark: bool, + pub specialization: bool, } impl Features { @@ -608,6 +612,7 @@ impl Features { stmt_expr_attributes: false, deprecated: false, question_mark: false, + specialization: false, } } } @@ -1102,6 +1107,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { } fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + if ii.defaultness == ast::Defaultness::Default { + self.gate_feature("specialization", + ii.span, + "specialization is unstable"); + } + match ii.node { ast::ImplItemKind::Const(..) => { self.gate_feature("associated_consts", @@ -1212,6 +1223,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &Handler, stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), deprecated: cx.has_feature("deprecated"), question_mark: cx.has_feature("question_mark"), + specialization: cx.has_feature("specialization"), } } From ed8d059d8d49491b736ea88edd8f2b57a866c44a Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 30 Dec 2015 15:25:31 -0800 Subject: [PATCH 18/37] Adjust tests for feature gate, and add tests for the gate itself --- src/test/auxiliary/go_trait.rs | 2 ++ .../auxiliary/specialization_cross_crate.rs | 2 ++ .../specialization-default-projection.rs | 2 ++ .../specialization-default-types.rs | 2 ++ .../specialization-feature-gate-default.rs | 19 +++++++++++++++ .../specialization-feature-gate-overlap.rs | 23 +++++++++++++++++++ .../specialization-negative-impl.rs | 1 + .../compile-fail/specialization-no-default.rs | 2 ++ .../specialization-overlap-negative.rs | 1 + .../compile-fail/specialization-overlap.rs | 2 ++ .../specialization-allowed-cross-crate.rs | 2 ++ src/test/run-pass/specialization-assoc-fns.rs | 2 ++ src/test/run-pass/specialization-basics.rs | 2 ++ .../run-pass/specialization-cross-crate.rs | 2 ++ .../specialization-default-methods.rs | 2 ++ .../run-pass/specialization-on-projection.rs | 2 ++ .../run-pass/specialization-projection.rs | 2 ++ .../run-pass/specialization-super-traits.rs | 2 ++ ...ation-translate-projections-with-params.rs | 2 ++ .../specialization-translate-projections.rs | 2 ++ 20 files changed, 76 insertions(+) create mode 100644 src/test/compile-fail/specialization-feature-gate-default.rs create mode 100644 src/test/compile-fail/specialization-feature-gate-overlap.rs diff --git a/src/test/auxiliary/go_trait.rs b/src/test/auxiliary/go_trait.rs index ab1051c56501f..044bb606b40e2 100644 --- a/src/test/auxiliary/go_trait.rs +++ b/src/test/auxiliary/go_trait.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // Common code used for tests that model the Fn/FnMut/FnOnce hierarchy. pub trait Go { diff --git a/src/test/auxiliary/specialization_cross_crate.rs b/src/test/auxiliary/specialization_cross_crate.rs index 92c985a3e1dde..1d235336de821 100644 --- a/src/test/auxiliary/specialization_cross_crate.rs +++ b/src/test/auxiliary/specialization_cross_crate.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + pub trait Foo { fn foo(&self) -> &'static str; } diff --git a/src/test/compile-fail/specialization-default-projection.rs b/src/test/compile-fail/specialization-default-projection.rs index b85dba126e613..3f85a503f62db 100644 --- a/src/test/compile-fail/specialization-default-projection.rs +++ b/src/test/compile-fail/specialization-default-projection.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // Make sure we can't project defaulted associated types trait Foo { diff --git a/src/test/compile-fail/specialization-default-types.rs b/src/test/compile-fail/specialization-default-types.rs index f6b2dd46efc29..dce1db06a92b3 100644 --- a/src/test/compile-fail/specialization-default-types.rs +++ b/src/test/compile-fail/specialization-default-types.rs @@ -12,6 +12,8 @@ // associated type in the impl defining it -- otherwise, what happens // if it's overridden? +#![feature(specialization)] + trait Example { type Output; fn generate(self) -> Self::Output; diff --git a/src/test/compile-fail/specialization-feature-gate-default.rs b/src/test/compile-fail/specialization-feature-gate-default.rs new file mode 100644 index 0000000000000..caac21278d464 --- /dev/null +++ b/src/test/compile-fail/specialization-feature-gate-default.rs @@ -0,0 +1,19 @@ +// 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. + +trait Foo { + fn foo(&self); +} + +impl Foo for T { + default fn foo(&self) {} //~ ERROR +} + +fn main() {} diff --git a/src/test/compile-fail/specialization-feature-gate-overlap.rs b/src/test/compile-fail/specialization-feature-gate-overlap.rs new file mode 100644 index 0000000000000..a7918e4426d9a --- /dev/null +++ b/src/test/compile-fail/specialization-feature-gate-overlap.rs @@ -0,0 +1,23 @@ +// 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. + +trait Foo { + fn foo(&self); +} + +impl Foo for T { + fn foo(&self) {} +} + +impl Foo for u8 { //~ ERROR + fn foo(&self) {} +} + +fn main() {} diff --git a/src/test/compile-fail/specialization-negative-impl.rs b/src/test/compile-fail/specialization-negative-impl.rs index d0d698d12e09c..3a907dc3b8e77 100644 --- a/src/test/compile-fail/specialization-negative-impl.rs +++ b/src/test/compile-fail/specialization-negative-impl.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(specialization)] struct TestType(T); diff --git a/src/test/compile-fail/specialization-no-default.rs b/src/test/compile-fail/specialization-no-default.rs index 3e23c6e06eafb..143c6f0e8582f 100644 --- a/src/test/compile-fail/specialization-no-default.rs +++ b/src/test/compile-fail/specialization-no-default.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + trait Foo { fn foo(&self); fn bar(&self); diff --git a/src/test/compile-fail/specialization-overlap-negative.rs b/src/test/compile-fail/specialization-overlap-negative.rs index cc427b4fed211..62a6d8d9b5031 100644 --- a/src/test/compile-fail/specialization-overlap-negative.rs +++ b/src/test/compile-fail/specialization-overlap-negative.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(specialization)] trait MyTrait {} diff --git a/src/test/compile-fail/specialization-overlap.rs b/src/test/compile-fail/specialization-overlap.rs index 7d14e85fba83d..57529d2ae4266 100644 --- a/src/test/compile-fail/specialization-overlap.rs +++ b/src/test/compile-fail/specialization-overlap.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + trait Foo {} impl Foo for T {} impl Foo for Vec {} //~ ERROR E0119 diff --git a/src/test/run-pass/specialization-allowed-cross-crate.rs b/src/test/run-pass/specialization-allowed-cross-crate.rs index 139c63d3cdb49..6b999f3835835 100644 --- a/src/test/run-pass/specialization-allowed-cross-crate.rs +++ b/src/test/run-pass/specialization-allowed-cross-crate.rs @@ -10,6 +10,8 @@ // aux-build:go_trait.rs +#![feature(specialization)] + extern crate go_trait; use go_trait::{Go,GoMut}; diff --git a/src/test/run-pass/specialization-assoc-fns.rs b/src/test/run-pass/specialization-assoc-fns.rs index 750581ffe9234..683e0d55dc944 100644 --- a/src/test/run-pass/specialization-assoc-fns.rs +++ b/src/test/run-pass/specialization-assoc-fns.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + trait Foo { fn mk() -> Self; } diff --git a/src/test/run-pass/specialization-basics.rs b/src/test/run-pass/specialization-basics.rs index e585c8e235354..b11495e9edf1a 100644 --- a/src/test/run-pass/specialization-basics.rs +++ b/src/test/run-pass/specialization-basics.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // Tests a variety of basic specialization scenarios and method // dispatch for them. diff --git a/src/test/run-pass/specialization-cross-crate.rs b/src/test/run-pass/specialization-cross-crate.rs index c74b1f2e49e43..7593ac4fb1dd2 100644 --- a/src/test/run-pass/specialization-cross-crate.rs +++ b/src/test/run-pass/specialization-cross-crate.rs @@ -10,6 +10,8 @@ // aux-build:specialization_cross_crate.rs +#![feature(specialization)] + extern crate specialization_cross_crate; use specialization_cross_crate::*; diff --git a/src/test/run-pass/specialization-default-methods.rs b/src/test/run-pass/specialization-default-methods.rs index 7482d3acbc1ac..9c52ceb524724 100644 --- a/src/test/run-pass/specialization-default-methods.rs +++ b/src/test/run-pass/specialization-default-methods.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // First, test only use of explicit `default` items: trait Foo { diff --git a/src/test/run-pass/specialization-on-projection.rs b/src/test/run-pass/specialization-on-projection.rs index f206153266222..65cbb31d22147 100644 --- a/src/test/run-pass/specialization-on-projection.rs +++ b/src/test/run-pass/specialization-on-projection.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + trait Foo {} trait Assoc { diff --git a/src/test/run-pass/specialization-projection.rs b/src/test/run-pass/specialization-projection.rs index 86fcab1fdc284..d26d59896a574 100644 --- a/src/test/run-pass/specialization-projection.rs +++ b/src/test/run-pass/specialization-projection.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // Make sure we *can* project non-defaulted associated types // cf compile-fail/specialization-default-projection.rs diff --git a/src/test/run-pass/specialization-super-traits.rs b/src/test/run-pass/specialization-super-traits.rs index 4a30e6bcd25f8..a9b3bfca53d18 100644 --- a/src/test/run-pass/specialization-super-traits.rs +++ b/src/test/run-pass/specialization-super-traits.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // Test that you can specialize via an explicit trait hierarchy // FIXME: this doesn't work yet... diff --git a/src/test/run-pass/specialization-translate-projections-with-params.rs b/src/test/run-pass/specialization-translate-projections-with-params.rs index 6bca8939d82a9..3d90bc56f42bd 100644 --- a/src/test/run-pass/specialization-translate-projections-with-params.rs +++ b/src/test/run-pass/specialization-translate-projections-with-params.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + trait Trait { fn convert(&self) -> T; } diff --git a/src/test/run-pass/specialization-translate-projections.rs b/src/test/run-pass/specialization-translate-projections.rs index 7f41729a527da..d224efe8c3183 100644 --- a/src/test/run-pass/specialization-translate-projections.rs +++ b/src/test/run-pass/specialization-translate-projections.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + use std::convert::Into; trait Trait { From c1df41e776c5a65ce8345d34a7e22296a99abd5e Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 30 Dec 2015 15:57:49 -0800 Subject: [PATCH 19/37] Add some basic comments about how specialization fits into the rest of the trait machinery --- src/librustc/middle/traits/README.md | 40 ++++++++++++++++++++++++ src/librustc/middle/traits/specialize.rs | 3 ++ 2 files changed, 43 insertions(+) diff --git a/src/librustc/middle/traits/README.md b/src/librustc/middle/traits/README.md index 92982af92dcfe..ff72f9dd07e36 100644 --- a/src/librustc/middle/traits/README.md +++ b/src/librustc/middle/traits/README.md @@ -428,3 +428,43 @@ We used to try and draw finer-grained distinctions, but that led to a serious of annoying and weird bugs like #22019 and #18290. This simple rule seems to be pretty clearly safe and also still retains a very high hit rate (~95% when compiling rustc). + +# Specialization + +Defined in the `specialize` module. + +The basic strategy is to build up a *specialization graph* during +coherence checking. Insertion into the graph locates the right place +to put an impl in the specialization hierarchy; if there is no right +place (due to partial overlap but no containment), you get an overlap +error. Specialization is consulted when selecting an impl (of course), +and the graph is consulted when propagating defaults down the +specialization hierarchy. + +You might expect that the specialization graph would be used during +selection -- i.e., when actually performing specialization. This is +not done for two reasons: + +- It's merely an optimization: given a set of candidates that apply, + we can determine the most specialized one by comparing them directly + for specialization, rather than consulting the graph. Given that we + also cache the results of selection, the benefit of this + optimization is questionable. + +- To build the specialization graph in the first place, we need to use + selection (because we need to determine whether one impl specializes + another). Dealing with this reentrancy would require some additional + mode switch for selection. Given that there seems to be no strong + reason to use the graph anyway, we stick with a simpler approach in + selection, and use the graph only for propagating default + implementations. + +Trait impl selection can succeed even when multiple impls can apply, +as long as they are part of the same specialization family. In that +case, it returns a *single* impl on success -- this is the most +specialized impl *known* to apply. However, if there are any inference +variables in play, the returned impl may not be the actual impl we +will use at trans time. Thus, we take special care to avoid projecting +associated types unless either (1) the associated type does not use +`default` and thus cannot be overridden or (2) all input types are +known concretely. diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index 0f47e19eaf3f4..30897e9289d9b 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -13,6 +13,9 @@ // // At the moment, this implementation support only the simple "chain" rule: // If any two impls overlap, one must be a strict subset of the other. +// +// See traits/README.md for a bit more detail on how specialization +// fits together with the rest of the trait machinery. use super::util; use super::SelectionContext; From 9734406a5f95393e8e888cf67b48861ea8a39de7 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 29 Jan 2016 16:25:18 -0800 Subject: [PATCH 20/37] Assorted fixed after rebasing --- src/librustc/middle/subst.rs | 4 +- src/librustc/middle/traits/select.rs | 1 - src/librustc/middle/traits/specialize.rs | 2 +- src/librustc/middle/ty/trait_def.rs | 9 +++- src/librustc_trans/trans/meth.rs | 3 +- src/librustc_typeck/check/mod.rs | 20 ++++---- src/librustc_typeck/coherence/overlap.rs | 46 ++++++++----------- src/librustc_typeck/collect.rs | 3 +- src/libsyntax/parse/parser.rs | 12 ++--- .../coherence-projection-conflict-orphan.rs | 4 +- .../coherence-projection-conflict-ty-param.rs | 4 +- .../coherence-projection-conflict.rs | 4 +- 12 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 9f8e2a22133ef..510a3dd454b95 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -162,9 +162,9 @@ impl<'tcx> Substs<'tcx> { pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> { let Substs { types, regions } = self; - let types = types.with_vec(FnSpace, other.types.get_slice(FnSpace).to_vec()); + let types = types.with_slice(FnSpace, other.types.get_slice(FnSpace)); let regions = regions.map(|r| { - r.with_vec(FnSpace, other.regions().get_slice(FnSpace).to_vec()) + r.with_slice(FnSpace, other.regions().get_slice(FnSpace)) }); Substs { types: types, regions: regions } } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index d319ac0219c75..fd41007d0e2e9 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1621,7 +1621,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { true }, ParamCandidate(..) => false, - ErrorCandidate => false // propagate errors }, ImplCandidate(other_def) => { // See if we can toss out `victim` based on specialization. diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index 30897e9289d9b..91ddcc3b9e471 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -87,7 +87,7 @@ impl SpecializationGraph { for slot in possible_siblings.iter_mut() { let possible_sibling = *slot; - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); if let Some(trait_ref) = overlap { diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index 582b289455196..737bb873d6d89 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -10,9 +10,10 @@ use dep_graph::DepNode; use middle::def_id::DefId; +use middle::traits; use middle::ty; use middle::ty::fast_reject; -use middle::ty::{Ty, TyCtxt}; +use middle::ty::{Ty, TyCtxt, TraitRef}; use std::borrow::{Borrow}; use std::cell::{Cell, Ref, RefCell}; use syntax::ast::Name; @@ -128,6 +129,12 @@ impl<'tcx> TraitDef<'tcx> { debug!("TraitDef::record_impl for {:?}, from {:?}", self, impl_trait_ref); + // Record the write into the impl set, but only for local + // impls: external impls are handled differently. + if impl_def_id.is_local() { + self.write_trait_impls(tcx); + } + // We don't want to borrow_mut after we already populated all impls, // so check if an impl is present with an immutable borrow first. if let Some(sty) = fast_reject::simplify_type(tcx, diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 073ef9797ed2a..072c1dfaa1d2a 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -33,7 +33,8 @@ use trans::glue; use trans::machine; use trans::type_::Type; use trans::type_of::*; -use middle::ty::{self, Ty, TyCtxt}; +use middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use middle::ty::MethodCall; use syntax::ast::{self, Name}; use syntax::attr; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index be2f63d1d1bc9..51b88612fe442 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -872,22 +872,24 @@ fn check_specialization_validity<'tcx, F>(tcx: &ty::ctxt<'tcx>, { let parent_item_opt = traits::get_parent_impl_item(tcx, impl_id, f); if let Some((Defaultness::Final, parent_impl)) = parent_item_opt { - span_err!(tcx.sess, impl_item.span, E0520, - "item `{}` is provided by an implementation that \ - specializes another, but the item in the parent \ - implementations is not marked `default` and so it \ - cannot be specialized.", - impl_item.name); + let mut err = struct_span_err!( + tcx.sess, impl_item.span, E0520, + "item `{}` is provided by an implementation that \ + specializes another, but the item in the parent \ + implementations is not marked `default` and so it \ + cannot be specialized.", + impl_item.name); match tcx.span_of_impl(parent_impl) { Ok(span) => { - span_note!(tcx.sess, span, "parent implementation is here:"); + err.span_note(span, "parent implementation is here:"); } Err(cname) => { - tcx.sess.note(&format!("parent implementation is in crate `{}`", - cname)); + err.note(&format!("parent implementation is in crate `{}`", cname)); } } + + err.emit(); } } diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 97cdcd4ba34e5..8f125abaec0bb 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -20,11 +20,10 @@ use syntax::ast; use rustc::dep_graph::DepNode; use rustc_front::hir; use rustc_front::intravisit; -use util::nodemap::{DefIdMap, DefIdSet}; +use util::nodemap::DefIdMap; pub fn check(tcx: &TyCtxt) { let mut overlap = OverlapChecker { tcx: tcx, - traits_checked: DefIdSet(), default_impls: DefIdMap() }; // this secondary walk specifically checks for some other cases, @@ -35,14 +34,6 @@ pub fn check(tcx: &TyCtxt) { struct OverlapChecker<'cx, 'tcx:'cx> { tcx: &'cx TyCtxt<'tcx>, - // The set of traits where we have checked for overlap. This is - // used to avoid checking the same trait twice. - // - // NB. It's ok to skip tracking this set because we fully - // encapsulate it, and we always create a task - // (`CoherenceOverlapCheck`) corresponding to each entry. - traits_checked: DefIdSet, - // maps from a trait def-id to an impl id default_impls: DefIdMap, } @@ -120,20 +111,19 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); - self.check_for_overlapping_impls_of_trait(trait_ref.def_id); - let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id); if let Some(prev_id) = prev_default_impl { - span_err!(self.tcx.sess, - self.span_of_def_id(impl_def_id), E0519, - "redundant default implementations of trait `{}`:", - trait_ref); - span_note!(self.tcx.sess, - self.span_of_def_id(self.tcx.map.local_def_id(prev_id)), - "redundant implementation is here:"); + let mut err = struct_span_err!( + self.tcx.sess, + self.tcx.span_of_impl(impl_def_id).unwrap(), E0519, + "redundant default implementations of trait `{}`:", + trait_ref); + err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id)).unwrap(), + "redundant implementation is here:"); + err.emit(); } } - hir::ItemImpl(_, _, _, Some(_), ref self_ty, _) => { + hir::ItemImpl(_, _, _, Some(_), _, _) => { let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; @@ -162,20 +152,22 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { }).unwrap_or(String::new()) }; - span_err!(self.tcx.sess, self.span_of_def_id(impl_def_id), E0119, - "conflicting implementations of trait `{}`{}:", - overlap.on_trait_ref, - self_type); + let mut err = struct_span_err!( + self.tcx.sess, self.tcx.span_of_impl(impl_def_id).unwrap(), E0119, + "conflicting implementations of trait `{}`{}:", + overlap.on_trait_ref, + self_type); match self.tcx.span_of_impl(overlap.with_impl) { Ok(span) => { - span_note!(self.tcx.sess, span, "conflicting implementation is here:"); + err.span_note(span, "conflicting implementation is here:"); } Err(cname) => { - self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", - cname)); + err.note(&format!("conflicting implementation in crate `{}`", cname)); } } + + err.emit(); } // check for overlap with the automatic `impl Trait for Trait` diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 4a8ce70ccb926..831c9804d1b0d 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -69,7 +69,6 @@ use middle::resolve_lifetime; use middle::const_eval::{self, ConstVal}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace}; -use middle::traits; use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer}; use middle::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeScheme}; use middle::ty::{VariantKind}; @@ -871,7 +870,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.id, hir::Inherited, sig, - hir::Defaultness::Default + hir::Defaultness::Default, tcx.mk_self_type(), &trait_def.generics, &trait_predicates); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 969d39056aa75..6839f11cd709d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -654,12 +654,12 @@ impl<'a> Parser<'a> { } } - pub fn eat_contextual_keyword(&mut self, ident: Ident) -> PResult { + pub fn eat_contextual_keyword(&mut self, ident: Ident) -> bool { if self.check_contextual_keyword(ident) { - try!(self.bump()); - Ok(true) + self.bump(); + true } else { - Ok(false) + false } } @@ -5229,8 +5229,8 @@ impl<'a> Parser<'a> { } /// Parse defaultness: DEFAULT or nothing - fn parse_defaultness(&mut self) -> PResult { - if try!(self.eat_contextual_keyword(special_idents::DEFAULT)) { + fn parse_defaultness(&mut self) -> PResult<'a, Defaultness> { + if self.eat_contextual_keyword(special_idents::DEFAULT) { Ok(Defaultness::Default) } else { Ok(Defaultness::Final) diff --git a/src/test/compile-fail/coherence-projection-conflict-orphan.rs b/src/test/compile-fail/coherence-projection-conflict-orphan.rs index 3de7945439838..3ed3549de89aa 100644 --- a/src/test/compile-fail/coherence-projection-conflict-orphan.rs +++ b/src/test/compile-fail/coherence-projection-conflict-orphan.rs @@ -21,8 +21,8 @@ pub trait Bar { type Output: 'static; } -impl Foo for i32 { } //~ ERROR E0119 +impl Foo for i32 { } -impl Foo for A { } +impl Foo for A { } //~ ERROR E0119 fn main() {} diff --git a/src/test/compile-fail/coherence-projection-conflict-ty-param.rs b/src/test/compile-fail/coherence-projection-conflict-ty-param.rs index 6880f3e9a3cc9..f04902a70f68c 100644 --- a/src/test/compile-fail/coherence-projection-conflict-ty-param.rs +++ b/src/test/compile-fail/coherence-projection-conflict-ty-param.rs @@ -15,8 +15,8 @@ use std::marker::PhantomData; pub trait Foo

{} -impl > Foo

for Option {} //~ ERROR E0119 +impl > Foo

for Option {} -impl Foo for Option { } +impl Foo for Option { } //~ ERROR E0119 fn main() {} diff --git a/src/test/compile-fail/coherence-projection-conflict.rs b/src/test/compile-fail/coherence-projection-conflict.rs index 2236e71b53fff..6d3ab32f06f43 100644 --- a/src/test/compile-fail/coherence-projection-conflict.rs +++ b/src/test/compile-fail/coherence-projection-conflict.rs @@ -16,9 +16,9 @@ pub trait Bar { type Output: 'static; } -impl Foo for i32 { } //~ ERROR E0119 +impl Foo for i32 { } -impl Foo for A { } +impl Foo for A { } //~ ERROR E0119 impl Bar for i32 { type Output = i32; From 462c83e272e2ba268aaf11ef00e9d47c52011b90 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sat, 30 Jan 2016 09:25:25 -0800 Subject: [PATCH 21/37] Address basic nits from initial review --- src/librustc/middle/traits/specialize.rs | 17 ++++--- src/librustc/middle/ty/trait_def.rs | 20 +++++--- src/librustc_typeck/coherence/overlap.rs | 4 +- src/libsyntax/feature_gate.rs | 1 + .../specialization-negative-impl.rs | 2 + .../compile-fail/specialization-no-default.rs | 49 +++++++++++++++++++ .../specialization-default-methods.rs | 23 +++++++++ 7 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index 91ddcc3b9e471..2c501c1a48103 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -52,10 +52,9 @@ pub struct SpecializationGraph { } /// Information pertinent to an overlapping impl error. -pub struct Overlap<'tcx> { +pub struct Overlap<'a, 'tcx: 'a> { + pub in_context: InferCtxt<'a, 'tcx>, pub with_impl: DefId, - - /// NB: this TraitRef can contain inference variables! pub on_trait_ref: ty::TraitRef<'tcx>, } @@ -70,13 +69,13 @@ impl SpecializationGraph { /// 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<'tcx>(&mut self, - tcx: &ty::ctxt<'tcx>, - impl_def_id: DefId, - trait_ref: ty::TraitRef) - -> Result<(), Overlap<'tcx>> { + pub fn insert<'a, 'tcx>(&mut self, + tcx: &'a ty::ctxt<'tcx>, + impl_def_id: DefId) + -> Result<(), Overlap<'a, 'tcx>> { assert!(impl_def_id.is_local()); + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let mut parent = trait_ref.def_id; let mut my_children = vec![]; @@ -98,6 +97,7 @@ impl SpecializationGraph { return Err(Overlap { with_impl: possible_sibling, on_trait_ref: trait_ref, + in_context: infcx, }); } @@ -118,6 +118,7 @@ impl SpecializationGraph { return Err(Overlap { with_impl: possible_sibling, on_trait_ref: trait_ref, + in_context: infcx, }); } diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index 737bb873d6d89..85cea4d8096d6 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -125,7 +125,8 @@ impl<'tcx> TraitDef<'tcx> { fn record_impl(&self, tcx: &TyCtxt<'tcx>, impl_def_id: DefId, - impl_trait_ref: TraitRef<'tcx>) -> bool { + impl_trait_ref: TraitRef<'tcx>) + -> bool { debug!("TraitDef::record_impl for {:?}, from {:?}", self, impl_trait_ref); @@ -161,7 +162,9 @@ impl<'tcx> TraitDef<'tcx> { tcx: &TyCtxt<'tcx>, impl_def_id: DefId, impl_trait_ref: TraitRef<'tcx>) { - self.record_impl(tcx, impl_def_id, impl_trait_ref); + assert!(impl_def_id.is_local()); + let was_new = self.record_impl(tcx, impl_def_id, impl_trait_ref); + assert!(was_new); } /// Records a trait-to-implementation mapping for a non-local impl. @@ -174,6 +177,8 @@ impl<'tcx> TraitDef<'tcx> { impl_def_id: DefId, impl_trait_ref: TraitRef<'tcx>, parent_impl: DefId) { + assert!(!impl_def_id.is_local()); + // if the impl has not previously been recorded if self.record_impl(tcx, impl_def_id, impl_trait_ref) { // if the impl is non-local, it's placed directly into the @@ -186,15 +191,14 @@ impl<'tcx> TraitDef<'tcx> { /// Adds a local impl into the specialization graph, returning an error with /// overlap information if the impl overlaps but does not specialize an /// existing impl. - pub fn add_impl_for_specialization(&self, - tcx: &ctxt<'tcx>, - impl_def_id: DefId, - impl_trait_ref: TraitRef<'tcx>) - -> Result<(), traits::Overlap<'tcx>> { + pub fn add_impl_for_specialization<'a>(&self, + tcx: &'a ctxt<'tcx>, + impl_def_id: DefId) + -> Result<(), traits::Overlap<'a, 'tcx>> { assert!(impl_def_id.is_local()); self.specialization_graph.borrow_mut() - .insert(tcx, impl_def_id, impl_trait_ref) + .insert(tcx, impl_def_id) } /// Returns the immediately less specialized impl, if any. diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 8f125abaec0bb..1ebc131224dc7 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -133,9 +133,7 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { let def = self.tcx.lookup_trait_def(trait_def_id); // attempt to insert into the specialization graph - let insert_result = def.add_impl_for_specialization(self.tcx, - impl_def_id, - trait_ref); + let insert_result = def.add_impl_for_specialization(self.tcx, impl_def_id); // insertion failed due to overlap if let Err(overlap) = insert_result { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d50eb17c87b83..a5f0fbbd0940b 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -250,6 +250,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status ("question_mark", "1.9.0", Some(31436), Active) // impl specialization (RFC 1210) + // TODO: update with issue number (once it exists), before landing ("specialization", "1.7.0", None, Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) diff --git a/src/test/compile-fail/specialization-negative-impl.rs b/src/test/compile-fail/specialization-negative-impl.rs index 3a907dc3b8e77..9aec614012e4d 100644 --- a/src/test/compile-fail/specialization-negative-impl.rs +++ b/src/test/compile-fail/specialization-negative-impl.rs @@ -13,6 +13,8 @@ struct TestType(T); +// TODO: nail down the rules here with @nikomatsakis + unsafe impl Send for TestType {} impl !Send for TestType {} diff --git a/src/test/compile-fail/specialization-no-default.rs b/src/test/compile-fail/specialization-no-default.rs index 143c6f0e8582f..06a01f0ca0575 100644 --- a/src/test/compile-fail/specialization-no-default.rs +++ b/src/test/compile-fail/specialization-no-default.rs @@ -10,6 +10,10 @@ #![feature(specialization)] +//////////////////////////////////////////////////////////////////////////////// +// Test 1: one layer of specialization, multiple methods, missing `default` +//////////////////////////////////////////////////////////////////////////////// + trait Foo { fn foo(&self); fn bar(&self); @@ -28,6 +32,10 @@ impl Foo for u32 { fn bar(&self) {} //~ ERROR E0520 } +//////////////////////////////////////////////////////////////////////////////// +// Test 2: one layer of specialization, missing `default` on associated type +//////////////////////////////////////////////////////////////////////////////// + trait Bar { type T; } @@ -40,4 +48,45 @@ impl Bar for u8 { type T = (); //~ ERROR E0520 } +//////////////////////////////////////////////////////////////////////////////// +// Test 3a: multiple layers of specialization, missing interior `default` +//////////////////////////////////////////////////////////////////////////////// + +trait Baz { + fn baz(&self); +} + +impl Baz for T { + default fn baz(&self) {} +} + +impl Baz for T { + fn baz(&self) {} +} + +impl Baz for i32 { + fn baz(&self) {} +} + +//////////////////////////////////////////////////////////////////////////////// +// Test 3b: multiple layers of specialization, missing interior `default`, +// redundant `default` in bottom layer. +//////////////////////////////////////////////////////////////////////////////// + +trait Redundant { + fn redundant(&self); +} + +impl Redundant for T { + default fn redundant(&self) {} +} + +impl Redundant for T { + fn redundant(&self) {} +} + +impl Redundant for i32 { + default fn redundant(&self) {} +} + fn main() {} diff --git a/src/test/run-pass/specialization-default-methods.rs b/src/test/run-pass/specialization-default-methods.rs index 9c52ceb524724..d662c5bfa28d6 100644 --- a/src/test/run-pass/specialization-default-methods.rs +++ b/src/test/run-pass/specialization-default-methods.rs @@ -16,6 +16,12 @@ trait Foo { fn foo(&self) -> bool; } +// Specialization tree for Foo: +// +// T +// / \ +// i32 i64 + impl Foo for T { default fn foo(&self) -> bool { false } } @@ -38,6 +44,23 @@ trait Bar { fn bar(&self) -> i32 { 0 } } +// Specialization tree for Bar. +// Uses of $ designate that method is provided +// +// $Bar (the trait) +// | +// T +// /|\ +// / | \ +// / | \ +// / | \ +// / | \ +// / | \ +// $i32 &str $Vec +// /\ +// / \ +// Vec $Vec + impl Bar for T {} // use the provided method impl Bar for i32 { From 940adda2aef63ac820cf331ebb7f6f0699c12045 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 16 Feb 2016 10:36:47 -0800 Subject: [PATCH 22/37] Move specialization graph walks to iterators; make associated type projection sensitive to "mode" (most importantly, trans vs middle). This commit introduces several pieces of iteration infrastructure in the specialization graph data structure, as well as various helpers for finding the definition of a given item, given its kind and name. In addition, associated type projection is now *mode-sensitive*, with three possible modes: - **Topmost**. This means that projection is only possible if there is a non-`default` definition of the associated type directly on the selected impl. This mode is a bit of a hack: it's used during early coherence checking before we have built the specialization graph (and therefore before we can walk up the specialization parents to find other definitions). Eventually, this should be replaced with a less "staged" construction of the specialization graph. - **AnyFinal**. Projection succeeds for any non-`default` associated type definition, even if it is defined by a parent impl. Used throughout typechecking. - **Any**. Projection always succeeds. Used by trans. The lasting distinction here is between `AnyFinal` and `Any` -- we wish to treat `default` associated types opaquely for typechecking purposes. In addition to the above, the commit includes a few other minor review fixes. --- src/librustc/middle/traits/coherence.rs | 3 +- src/librustc/middle/traits/mod.rs | 8 +- src/librustc/middle/traits/project.rs | 145 ++++-- src/librustc/middle/traits/select.rs | 146 +++++- src/librustc/middle/traits/specialize.rs | 454 ------------------ src/librustc/middle/traits/specialize/mod.rs | 244 ++++++++++ .../traits/specialize/specialization_graph.rs | 382 +++++++++++++++ src/librustc/middle/ty/trait_def.rs | 14 +- .../obligation_forest/README.md | 4 +- src/librustc_front/hir.rs | 10 + src/librustc_metadata/encoder.rs | 9 +- src/librustc_trans/trans/common.rs | 4 +- src/librustc_trans/trans/meth.rs | 26 +- src/librustc_typeck/check/mod.rs | 180 ++++--- src/librustc_typeck/coherence/overlap.rs | 6 +- .../compile-fail/specialization-no-default.rs | 4 +- 16 files changed, 989 insertions(+), 650 deletions(-) delete mode 100644 src/librustc/middle/traits/specialize.rs create mode 100644 src/librustc/middle/traits/specialize/mod.rs create mode 100644 src/librustc/middle/traits/specialize/specialization_graph.rs diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 33a1e3816e348..bc3d3e48d6b1a 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -10,6 +10,7 @@ //! See `README.md` for high-level documentation +use super::build_selcx; use super::{SelectionContext, Obligation, ObligationCause}; use super::util; @@ -36,7 +37,7 @@ pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>, impl1_def_id, impl2_def_id); - let selcx = &mut SelectionContext::intercrate(infcx); + let selcx = &mut build_selcx(infcx).project_topmost().intercrate().build(); overlap(selcx, impl1_def_id, impl2_def_id) } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 180a1312e2500..d9fc53ff2c261 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -45,13 +45,11 @@ pub use self::object_safety::object_safety_violations; pub use self::object_safety::ObjectSafetyViolation; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::is_vtable_safe_method; -pub use self::select::EvaluationCache; -pub use self::select::SelectionContext; -pub use self::select::SelectionCache; +pub use self::select::{EvaluationCache, SelectionContextBuilder, build_selcx}; +pub use self::select::{ProjectionMode, SelectionContext, SelectionCache}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants -pub use self::specialize::{Overlap, SpecializationGraph, specializes}; -pub use self::specialize::{ItemSource, get_impl_item_or_default, get_parent_impl_item}; +pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index dc279aae32cbb..20d6c0b6fc038 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -11,8 +11,9 @@ //! Code for projecting associated types out of trait references. use super::elaborate_predicates; -use super::get_impl_item_or_default; use super::report_overflow_error; +use super::specialization_graph; +use super::translate_substs; use super::Obligation; use super::ObligationCause; use super::PredicateObligation; @@ -22,14 +23,18 @@ use super::VtableClosureData; use super::VtableImplData; use super::util; +use middle::def_id::DefId; use middle::infer::{self, TypeOrigin}; use middle::subst::Subst; use middle::ty::{self, ToPredicate, RegionEscape, HasTypeFlags, ToPolyTraitRef, Ty, TyCtxt}; use middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_front::hir; use syntax::parse::token; +use syntax::ast; use util::common::FN_OUTPUT_NAME; +use std::rc::Rc; + pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -568,7 +573,49 @@ fn project_type<'cx,'tcx>( assert!(candidates.vec.len() <= 1); - match candidates.vec.pop() { + let possible_candidate = candidates.vec.pop().and_then(|candidate| { + // In Any (i.e. trans) mode, all projections succeed; + // otherwise, we need to be sensitive to `default` and + // specialization. + if !selcx.projection_mode().any() { + if let ProjectionTyCandidate::Impl(ref impl_data) = candidate { + if let Some(node_item) = assoc_ty_def(selcx, + impl_data.impl_def_id, + obligation.predicate.item_name) { + if node_item.node.is_from_trait() { + if node_item.item.ty.is_some() { + // If the associated type has a default from the + // trait, that should be considered `default` and + // hence not projected. + // + // Note, however, that we allow a projection from + // the trait specifically in the case that the trait + // does *not* give a default. This is purely to + // avoid spurious errors: the situation can only + // arise when *no* impl in the specialization chain + // has provided a definition for the type. When we + // confirm the candidate, we'll turn the projection + // into a TyError, since the actual error will be + // reported in `check_impl_items_against_trait`. + return None; + } + } else if node_item.item.defaultness.is_default() { + return None; + } + } else { + // Normally this situation could only arise througha + // compiler bug, but at coherence-checking time we only look + // at the topmost impl (we don't even consider the trait + // itself) for the definition -- so we can fail to find a + // definition of the type even if it exists. + return None; + } + } + } + Some(candidate) + }); + + match possible_candidate { Some(candidate) => { let (ty, obligations) = confirm_candidate(selcx, obligation, candidate); Ok(ProjectedTy::Progress(ty, obligations)) @@ -744,28 +791,6 @@ fn assemble_candidates_from_impls<'cx,'tcx>( match vtable { super::VtableImpl(data) => { - if data.substs.types.needs_infer() { - let assoc_ty_opt = get_impl_item_or_default(selcx.tcx(), data.impl_def_id, |cand| { - if let &ty::TypeTraitItem(ref assoc_ty) = cand { - if assoc_ty.name == obligation.predicate.item_name { - return Some(assoc_ty.defaultness); - } - } - None - }); - - if let Some((defaultness, source)) = assoc_ty_opt { - if !source.is_from_trait() && defaultness == hir::Defaultness::Default { - // FIXME: is it OK to not mark as ambiguous? - return Ok(()); - } - } else { - selcx.tcx().sess.span_bug(obligation.cause.span, - &format!("No associated type for {:?}", - obligation_trait_ref)); - } - } - debug!("assemble_candidates_from_impls: impl candidate {:?}", data); @@ -967,29 +992,59 @@ fn confirm_impl_candidate<'cx,'tcx>( { let VtableImplData { substs, nested, impl_def_id } = impl_vtable; - get_impl_item_or_default(selcx.tcx(), impl_def_id, |cand| { - if let &ty::TypeTraitItem(ref assoc_ty) = cand { - if assoc_ty.name == obligation.predicate.item_name { - if let Some(ty) = assoc_ty.ty { - return Some(ty) - } else { - // This means that the impl is missing a definition for the - // associated type. This error will be reported by the type - // checker method `check_impl_items_against_trait`, so here - // we just return TyError. - debug!("confirm_impl_candidate: no associated type {:?} for {:?}", - assoc_ty.name, - obligation.predicate.trait_ref); - return Some(selcx.tcx().types.err); + let tcx = selcx.tcx(); + let trait_ref = obligation.predicate.trait_ref; + let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_name); + + match assoc_ty { + Some(node_item) => { + let ty = node_item.item.ty.unwrap_or_else(|| { + // This means that the impl is missing a definition for the + // associated type. This error will be reported by the type + // checker method `check_impl_items_against_trait`, so here we + // just return TyError. + debug!("confirm_impl_candidate: no associated type {:?} for {:?}", + node_item.item.name, + obligation.predicate.trait_ref); + tcx.types.err + }); + let substs = translate_substs(tcx, impl_def_id, substs, node_item.node); + (ty.subst(tcx, &substs), nested) + } + None => { + tcx.sess.span_bug(obligation.cause.span, + &format!("No associated type for {:?}", trait_ref)); + } + } +} + +/// Locate the definition of an associated type in the specialization hierarchy, +/// starting from the given impl. +/// +/// Based on the "projection mode", this lookup may in fact only examine the +/// topmost impl. See the comments for `ProjectionMode` for more details. +fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>, impl_def_id: DefId, assoc_ty_name: ast::Name) + -> Option>>> +{ + let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id; + + if selcx.projection_mode().topmost() { + let impl_node = specialization_graph::Node::Impl(impl_def_id); + for item in impl_node.items(selcx.tcx()) { + if let ty::TypeTraitItem(assoc_ty) = item { + if assoc_ty.name == assoc_ty_name { + return Some(specialization_graph::NodeItem { + node: specialization_graph::Node::Impl(impl_def_id), + item: assoc_ty, + }); } } } None - }).map(|(ty, source)| { - (ty.subst(selcx.tcx(), &source.translate_substs(selcx.tcx(), substs)), nested) - }).unwrap_or_else(|| { - selcx.tcx().sess.span_bug(obligation.cause.span, - &format!("No associated type for {:?}", - obligation.predicate.trait_ref)); - }) + } else { + selcx.tcx().lookup_trait_def(trait_def_id) + .ancestors(impl_def_id) + .type_defs(selcx.tcx(), assoc_ty_name) + .next() + } } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index fd41007d0e2e9..83e02d16c3b81 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -76,8 +76,99 @@ pub struct SelectionContext<'cx, 'tcx:'cx> { /// other words, we consider `$0 : Bar` to be unimplemented if /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. - intercrate: bool, + + /// Sadly, the behavior of projection varies a bit depending on the + /// stage of compilation. The specifics are given in the + /// documentation for `ProjectionMode`. + projection_mode: ProjectionMode, +} + +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ProjectionMode { + /// At coherence-checking time, we're still constructing the + /// specialization graph, and thus we only project project + /// non-`default` associated types that are defined directly in + /// the applicable impl. (This behavior should be improved over + /// time, to allow for successful projections modulo cycles + /// between different impls). + // TODO: Add tracking issue to do better here. + /// + /// Here's an example that will fail due to the restriction: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// type Output = bool; + /// } + /// + /// impl Assoc for u8 {} // <- inherits the non-default type from above + /// + /// trait Foo {} + /// impl Foo for u32 {} + /// impl Foo for ::Output {} // <- this projection will fail + /// ``` + /// + /// The projection would succeed if `Output` had been defined + /// directly in the impl for `u8`. + // TODO: Add test + Topmost, + + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let <() as Assoc>::Output = true; + /// } + // TODO: Add test + AnyFinal, + + /// At trans time, all projections will succeed. + Any, +} + +impl ProjectionMode { + pub fn topmost(&self) -> bool { + match *self { + ProjectionMode::Topmost => true, + _ => false, + } + } + + pub fn any_final(&self) -> bool { + match *self { + ProjectionMode::AnyFinal => true, + _ => false, + } + } + + pub fn any(&self) -> bool { + match *self { + ProjectionMode::Any => true, + _ => false, + } + } } // A stack that walks back up the stack frame. @@ -257,22 +348,45 @@ pub struct EvaluationCache<'tcx> { hashmap: RefCell, EvaluationResult>> } -impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) - -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx: infcx, - freshener: infcx.freshener(), - intercrate: false, - } +pub struct SelectionContextBuilder<'cx, 'tcx: 'cx>(SelectionContext<'cx, 'tcx>); + +impl<'cx, 'tcx> SelectionContextBuilder<'cx, 'tcx> { + pub fn intercrate(mut self) -> Self { + self.0.intercrate = true; + self } - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) - -> SelectionContext<'cx, 'tcx> { + pub fn project_any(mut self) -> Self { + self.0.projection_mode = ProjectionMode::Any; + self + } + + pub fn project_any_final(mut self) -> Self { + self.0.projection_mode = ProjectionMode::AnyFinal; + self + } + + pub fn project_topmost(mut self) -> Self { + self.0.projection_mode = ProjectionMode::Topmost; + self + } + + pub fn build(self) -> SelectionContext<'cx, 'tcx> { + self.0 + } +} + +pub fn build_selcx<'cx, 'tcx>(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContextBuilder<'cx, 'tcx> { + SelectionContextBuilder(SelectionContext::new(infcx)) +} + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx: infcx, freshener: infcx.freshener(), - intercrate: true, + intercrate: false, + projection_mode: ProjectionMode::AnyFinal, } } @@ -292,6 +406,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx } + pub fn projection_mode(&self) -> ProjectionMode { + self.projection_mode + } + /////////////////////////////////////////////////////////////////////////// // Selection // @@ -565,7 +683,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // this crate, perhaps the type would be unified with // something from another crate that does provide an impl. // - // In intracrate mode, we must still be conservative. The reason is + // In intra mode, we must still be conservative. The reason is // that we want to avoid cycles. Imagine an impl like: // // impl Eq for Vec @@ -780,7 +898,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if !self.is_knowable(stack) { - debug!("intercrate not knowable"); + debug!("coherence stage: not knowable"); return Ok(None); } diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs deleted file mode 100644 index 2c501c1a48103..0000000000000 --- a/src/librustc/middle/traits/specialize.rs +++ /dev/null @@ -1,454 +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. - -// Logic and data structures related to impl specialization, explained in -// greater detail below. -// -// At the moment, this implementation support only the simple "chain" rule: -// If any two impls overlap, one must be a strict subset of the other. -// -// See traits/README.md for a bit more detail on how specialization -// fits together with the rest of the trait machinery. - -use super::util; -use super::SelectionContext; - -use middle::cstore::CrateStore; -use middle::def_id::DefId; -use middle::infer::{self, InferCtxt, TypeOrigin}; -use middle::region; -use middle::subst::{Subst, Substs}; -use middle::traits; -use middle::ty::{self, ImplOrTraitItem}; -use syntax::codemap::DUMMY_SP; -use util::nodemap::DefIdMap; - -/// A per-trait graph of impls in specialization order. -/// -/// The graph provides two key services: -/// -/// - Construction, which implicitly checks for overlapping impls (i.e., impls -/// that overlap but where neither specializes the other -- an artifact of the -/// simple "chain" rule. -/// -/// - Parent extraction. In particular, the graph can give you the *immediate* -/// parents of a given specializing impl, which is needed for extracting -/// default items amongst other thigns. In the simple "chain" rule, every impl -/// has at most one parent. -pub struct SpecializationGraph { - // all impls have a parent; the "root" impls have as their parent the def_id - // of the trait - parent: DefIdMap, - - // the "root" impls are found by looking up the trait's def_id. - children: DefIdMap>, -} - -/// Information pertinent to an overlapping impl error. -pub struct Overlap<'a, 'tcx: 'a> { - pub in_context: InferCtxt<'a, 'tcx>, - pub with_impl: DefId, - pub on_trait_ref: ty::TraitRef<'tcx>, -} - -impl SpecializationGraph { - pub fn new() -> SpecializationGraph { - SpecializationGraph { - parent: Default::default(), - children: Default::default(), - } - } - - /// 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<'a, 'tcx>(&mut self, - tcx: &'a ty::ctxt<'tcx>, - impl_def_id: DefId) - -> Result<(), Overlap<'a, 'tcx>> { - assert!(impl_def_id.is_local()); - - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let mut parent = trait_ref.def_id; - let mut my_children = vec![]; - - // descend the existing tree, looking for the right location to add this impl - 'descend: loop { - let mut possible_siblings = self.children.entry(parent).or_insert(vec![]); - - for slot in possible_siblings.iter_mut() { - let possible_sibling = *slot; - - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); - let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); - - if let Some(trait_ref) = overlap { - if !tcx.sess.features.borrow().specialization { - // if specialization is not turned on, all overlaps - // should immediately trigger an error - - return Err(Overlap { - with_impl: possible_sibling, - on_trait_ref: trait_ref, - in_context: infcx, - }); - } - - let le = specializes(tcx, impl_def_id, possible_sibling); - let ge = specializes(tcx, possible_sibling, impl_def_id); - - if le && !ge { - // the impl specializes possible_sibling - parent = possible_sibling; - continue 'descend; - } else if ge && !le { - // possible_sibling specializes the impl - *slot = impl_def_id; - self.parent.insert(possible_sibling, impl_def_id); - my_children.push(possible_sibling); - } else { - // overlap, but no specialization; error out - return Err(Overlap { - with_impl: possible_sibling, - on_trait_ref: trait_ref, - in_context: infcx, - }); - } - - break 'descend; - } - } - - // no overlap with any potential siblings, so add as a new sibling - self.parent.insert(impl_def_id, parent); - possible_siblings.push(impl_def_id); - break; - } - - if self.children.insert(impl_def_id, my_children).is_some() { - tcx.sess - .bug("When inserting an impl into the specialization graph, existing children for \ - the impl were already present."); - } - - Ok(()) - } - - /// Insert cached metadata mapping from a child impl back to its parent. - pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) { - if self.parent.insert(child, parent).is_some() { - panic!("When recording an impl from the crate store, information about its parent \ - was already present."); - } - - self.children.entry(parent).or_insert(vec![]).push(child); - } - - /// 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() - } -} - -/// When we have selected one impl, but are actually using item definitions from -/// a parent impl providing a default, we need a way to translate between the -/// type parameters of the two impls. Here the `source_impl` is the one we've -/// selected, and `source_substs` is a substitution of its generics (and possibly -/// some relevant `FnSpace` variables as well). And `target_impl` is the impl -/// we're actually going to get the definition from. -fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>, - source_impl: DefId, - source_substs: Substs<'tcx>, - target_impl: DefId) - -> Substs<'tcx> { - - // We need to build a subst that covers all the generics of - // `target_impl`. Start by introducing fresh infer variables: - let target_generics = tcx.lookup_item_type(target_impl).generics; - let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut target_substs = infcx.fresh_substs_for_generics(DUMMY_SP, &target_generics); - if source_substs.regions.is_erased() { - target_substs = target_substs.erase_regions() - } - - if !fulfill_implication(&mut infcx, - source_impl, - source_substs.clone(), - target_impl, - target_substs.clone()) { - tcx.sess - .bug("When translating substitutions for specialization, the expected specializaiton \ - failed to hold") - } - - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. We - // also carry along any FnSpace substitutions, which don't need to be - // adjusted when mapping from one impl to another. - infcx.resolve_type_vars_if_possible(&target_substs) - .with_method_from_subst(&source_substs) -} - -/// When we've selected an impl but need to use an item definition provided by -/// the trait itself, we need to translate the substitution applied to the impl -/// to one that makes sense for the trait. -fn translate_substs_from_impl_to_trait<'tcx>(tcx: &ty::ctxt<'tcx>, - source_impl: DefId, - source_substs: Substs<'tcx>) - -> Substs<'tcx> { - - let source_trait_ref = tcx.impl_trait_ref(source_impl).unwrap().subst(tcx, &source_substs); - - let mut new_substs = source_trait_ref.substs.clone(); - if source_substs.regions.is_erased() { - new_substs = new_substs.erase_regions() - } - - // Carry any FnSpace substitutions along; they don't need to be adjusted - new_substs.with_method_from_subst(&source_substs) -} - -#[derive(Debug, Copy, Clone)] -/// When looking up an item in an impl, it may turn out that the item -/// is actually provided as a default by a more generic impl, or by -/// the trait itself. This enum says where the item came from. -pub enum ItemSource { - Impl { - requested_impl: DefId, - actual_impl: DefId, - }, - Trait { - requested_impl: DefId, - }, -} - -impl ItemSource { - pub fn is_from_trait(&self) -> bool { - match *self { - ItemSource::Trait { .. } => true, - _ => false, - } - } - - /// Given a subst for the requested impl, translate it to a subst - /// appropriate for the actual item definition (whether it be in that impl, - /// a parent impl, or the trait). - pub fn translate_substs<'tcx>(&self, - tcx: &ty::ctxt<'tcx>, - requested_impl_substs: Substs<'tcx>) - -> Substs<'tcx> { - match *self { - ItemSource::Impl { requested_impl, actual_impl } => { - // no need to translate if we're targetting the impl we started with - if requested_impl == actual_impl { - return requested_impl_substs; - } - - translate_substs_between_impls(tcx, - requested_impl, - requested_impl_substs, - actual_impl) - - } - ItemSource::Trait { requested_impl } => { - translate_substs_from_impl_to_trait(tcx, requested_impl, requested_impl_substs) - } - } - } -} - -/// Lookup the definition of an item within `requested_impl` or its specialization -/// parents, including provided items from the trait itself. -/// -/// The closure `f` works in the style of `filter_map`. -pub fn get_impl_item_or_default<'tcx, I, F>(tcx: &ty::ctxt<'tcx>, - requested_impl: DefId, - mut f: F) - -> Option<(I, ItemSource)> - where F: for<'a> FnMut(&ImplOrTraitItem<'tcx>) -> Option -{ - let impl_or_trait_items_map = tcx.impl_or_trait_items.borrow(); - let trait_def_id = tcx.trait_id_of_impl(requested_impl).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - // Walk up the specialization tree, looking for a matching item definition - - let mut current_impl = requested_impl; - loop { - for impl_item_id in &tcx.impl_items.borrow()[¤t_impl] { - let impl_item = &impl_or_trait_items_map[&impl_item_id.def_id()]; - if let Some(t) = f(impl_item) { - let source = ItemSource::Impl { - requested_impl: requested_impl, - actual_impl: current_impl, - }; - return Some((t, source)); - } - } - - if let Some(parent) = trait_def.parent_of_impl(current_impl) { - current_impl = parent; - } else { - break; - } - } - - // The item isn't defined anywhere in the hierarchy. Get the - // default from the trait. - - for trait_item in tcx.trait_items(trait_def_id).iter() { - if let Some(t) = f(trait_item) { - return Some((t, ItemSource::Trait { requested_impl: requested_impl })); - } - } - - None -} - -/// Convenience function for locating an item defined in a specialization parent, if any. -pub fn get_parent_impl_item<'tcx, I, F>(tcx: &ty::ctxt<'tcx>, - child_impl: DefId, - f: F) - -> Option<(I, DefId)> - where F: for<'a> FnMut(&ImplOrTraitItem<'tcx>) -> Option -{ - let trait_def_id = tcx.trait_id_of_impl(child_impl).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - trait_def.parent_of_impl(child_impl) - .and_then(|parent_impl| get_impl_item_or_default(tcx, parent_impl, f)) - .and_then(|(item, source)| { - match source { - ItemSource::Trait { .. } => None, - ItemSource::Impl { actual_impl, .. } => Some((item, actual_impl)), - } - }) -} - -fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { - let impl_generics = tcx.lookup_item_type(impl_def_id).generics; - - let types = impl_generics.types.map(|def| tcx.mk_param_from_def(def)); - - // FIXME: figure out what we actually want here - let regions = impl_generics.regions.map(|_| ty::Region::ReStatic); - // |d| infcx.next_region_var(infer::RegionVariableOrigin::EarlyBoundRegion(span, d.name))); - - Substs::new(types, regions) -} - -/// Is impl1 a specialization of impl2? -/// -/// Specialization is determined by the sets of types to which the impls apply; -/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies -/// to. -pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { - // We determine whether there's a subset relationship by: - // - // - skolemizing impl1, - // - instantiating impl2 with fresh inference variables, - // - assuming the where clauses for impl1, - // - unifying, - // - attempting to prove the where clauses for impl2 - // - // The last three steps are essentially checking for an implication between two impls - // after appropriate substitutions. This is what `fulfill_implication` checks for. - // - // See RFC 1210 for more details and justification. - - let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - - let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id); - let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); - - fulfill_implication(&mut infcx, - impl1_def_id, - impl1_substs, - impl2_def_id, - impl2_substs) -} - -/// Does impl1 (instantiated with the impl1_substs) imply impl2 -/// (instantiated with impl2_substs)? -/// -/// Mutates the `infcx` in two ways: -/// - by adding the obligations of impl1 to the parameter environment -/// - via fulfillment, so that if the implication holds the various unifications -fn fulfill_implication<'a, 'tcx>(infcx: &mut InferCtxt<'a, 'tcx>, - impl1_def_id: DefId, - impl1_substs: Substs<'tcx>, - impl2_def_id: DefId, - impl2_substs: Substs<'tcx>) - -> bool { - let tcx = &infcx.tcx; - - let (impl1_trait_ref, impl1_obligations) = { - let selcx = &mut SelectionContext::new(&infcx); - util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) - }; - - let impl1_predicates: Vec<_> = impl1_obligations.iter() - .cloned() - .map(|oblig| oblig.predicate) - .collect(); - - infcx.parameter_environment = ty::ParameterEnvironment { - tcx: tcx, - free_substs: impl1_substs, - implicit_region_bound: ty::ReEmpty, // FIXME: is this OK? - caller_bounds: impl1_predicates, - selection_cache: traits::SelectionCache::new(), - evaluation_cache: traits::EvaluationCache::new(), - free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK? - }; - - let selcx = &mut SelectionContext::new(&infcx); - let (impl2_trait_ref, impl2_obligations) = util::impl_trait_ref_and_oblig(selcx, - impl2_def_id, - &impl2_substs); - - // do the impls unify? If not, no specialization. - if let Err(_) = infer::mk_eq_trait_refs(&infcx, - true, - TypeOrigin::Misc(DUMMY_SP), - impl1_trait_ref, - impl2_trait_ref) { - debug!("fulfill_implication: {:?} does not unify with {:?}", - impl1_trait_ref, - impl2_trait_ref); - return false; - } - - let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); - - // attempt to prove all of the predicates for impl2 given those for impl1 - // (which are packed up in penv) - - for oblig in impl2_obligations.into_iter() { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } - - if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { - // no dice! - debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ - {:?}", - impl1_trait_ref, - impl2_trait_ref, - errors, - infcx.parameter_environment.caller_bounds); - false - } else { - debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", - impl1_trait_ref, - impl2_trait_ref); - true - } -} diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs new file mode 100644 index 0000000000000..2dc4926736e60 --- /dev/null +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -0,0 +1,244 @@ +// 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. + +// Logic and data structures related to impl specialization, explained in +// greater detail below. +// +// At the moment, this implementation support only the simple "chain" rule: +// If any two impls overlap, one must be a strict subset of the other. +// +// See traits/README.md for a bit more detail on how specialization +// fits together with the rest of the trait machinery. + +use super::{util, build_selcx, SelectionContext}; + +use middle::cstore::CrateStore; +use middle::def_id::DefId; +use middle::infer::{self, InferCtxt, TypeOrigin}; +use middle::region; +use middle::subst::{Subst, Substs}; +use middle::traits; +use middle::ty; +use syntax::codemap::DUMMY_SP; + +pub mod specialization_graph; + +/// Information pertinent to an overlapping impl error. +pub struct Overlap<'a, 'tcx: 'a> { + pub in_context: InferCtxt<'a, 'tcx>, + pub with_impl: DefId, + pub on_trait_ref: ty::TraitRef<'tcx>, +} + +/// Given a subst for the requested impl, translate it to a subst +/// appropriate for the actual item definition (whether it be in that impl, +/// a parent impl, or the trait). +pub fn translate_substs<'tcx>(tcx: &ty::ctxt<'tcx>, + from_impl: DefId, + from_impl_substs: Substs<'tcx>, + to_node: specialization_graph::Node) + -> Substs<'tcx> { + match to_node { + specialization_graph::Node::Impl(to_impl) => { + // no need to translate if we're targetting the impl we started with + if from_impl == to_impl { + return from_impl_substs; + } + + translate_substs_between_impls(tcx, from_impl, from_impl_substs, to_impl) + + } + specialization_graph::Node::Trait(..) => { + translate_substs_from_impl_to_trait(tcx, from_impl, from_impl_substs) + } + } +} + +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics (and possibly +/// some relevant `FnSpace` variables as well). And `target_impl` is the impl +/// we're actually going to get the definition from. +fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>, + target_impl: DefId) + -> Substs<'tcx> { + + // We need to build a subst that covers all the generics of + // `target_impl`. Start by introducing fresh infer variables: + let target_generics = tcx.lookup_item_type(target_impl).generics; + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let mut target_substs = infcx.fresh_substs_for_generics(DUMMY_SP, &target_generics); + if source_substs.regions.is_erased() { + target_substs = target_substs.erase_regions() + } + + if !fulfill_implication(&mut infcx, + source_impl, + source_substs.clone(), + target_impl, + target_substs.clone()) { + tcx.sess + .bug("When translating substitutions for specialization, the expected specializaiton \ + failed to hold") + } + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. We + // also carry along any FnSpace substitutions, which don't need to be + // adjusted when mapping from one impl to another. + infcx.resolve_type_vars_if_possible(&target_substs) + .with_method_from_subst(&source_substs) +} + +/// When we've selected an impl but need to use an item definition provided by +/// the trait itself, we need to translate the substitution applied to the impl +/// to one that makes sense for the trait. +fn translate_substs_from_impl_to_trait<'tcx>(tcx: &ty::ctxt<'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>) + -> Substs<'tcx> { + + let source_trait_ref = tcx.impl_trait_ref(source_impl).unwrap().subst(tcx, &source_substs); + + let mut new_substs = source_trait_ref.substs.clone(); + if source_substs.regions.is_erased() { + new_substs = new_substs.erase_regions() + } + + // Carry any FnSpace substitutions along; they don't need to be adjusted + new_substs.with_method_from_subst(&source_substs) +} + +fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { + let impl_generics = tcx.lookup_item_type(impl_def_id).generics; + + let types = impl_generics.types.map(|def| tcx.mk_param_from_def(def)); + + // FIXME: figure out what we actually want here + let regions = impl_generics.regions.map(|_| ty::Region::ReStatic); + // |d| infcx.next_region_var(infer::RegionVariableOrigin::EarlyBoundRegion(span, d.name))); + + Substs::new(types, regions) +} + +/// Is impl1 a specialization of impl2? +/// +/// Specialization is determined by the sets of types to which the impls apply; +/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies +/// to. +pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { + if !tcx.sess.features.borrow().specialization { + return false; + } + + // We determine whether there's a subset relationship by: + // + // - skolemizing impl1, + // - instantiating impl2 with fresh inference variables, + // - assuming the where clauses for impl1, + // - unifying, + // - attempting to prove the where clauses for impl2 + // + // The last three steps are essentially checking for an implication between two impls + // after appropriate substitutions. This is what `fulfill_implication` checks for. + // + // See RFC 1210 for more details and justification. + + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + + let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id); + let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); + + fulfill_implication(&mut infcx, + impl1_def_id, + impl1_substs, + impl2_def_id, + impl2_substs) +} + +/// Does impl1 (instantiated with the impl1_substs) imply impl2 +/// (instantiated with impl2_substs)? +/// +/// Mutates the `infcx` in two ways: +/// - by adding the obligations of impl1 to the parameter environment +/// - via fulfillment, so that if the implication holds the various unifications +fn fulfill_implication<'a, 'tcx>(infcx: &mut InferCtxt<'a, 'tcx>, + impl1_def_id: DefId, + impl1_substs: Substs<'tcx>, + impl2_def_id: DefId, + impl2_substs: Substs<'tcx>) + -> bool { + let tcx = &infcx.tcx; + + let (impl1_trait_ref, impl1_obligations) = { + let selcx = &mut SelectionContext::new(&infcx); + util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) + }; + + let impl1_predicates: Vec<_> = impl1_obligations.iter() + .cloned() + .map(|oblig| oblig.predicate) + .collect(); + + infcx.parameter_environment = ty::ParameterEnvironment { + tcx: tcx, + free_substs: impl1_substs, + implicit_region_bound: ty::ReEmpty, // FIXME: is this OK? + caller_bounds: impl1_predicates, + selection_cache: traits::SelectionCache::new(), + evaluation_cache: traits::EvaluationCache::new(), + free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK? + }; + + let selcx = &mut build_selcx(&infcx).project_topmost().build(); + let (impl2_trait_ref, impl2_obligations) = util::impl_trait_ref_and_oblig(selcx, + impl2_def_id, + &impl2_substs); + + // do the impls unify? If not, no specialization. + if let Err(_) = infer::mk_eq_trait_refs(&infcx, + true, + TypeOrigin::Misc(DUMMY_SP), + impl1_trait_ref, + impl2_trait_ref) { + debug!("fulfill_implication: {:?} does not unify with {:?}", + impl1_trait_ref, + impl2_trait_ref); + return false; + } + + let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); + + // attempt to prove all of the predicates for impl2 given those for impl1 + // (which are packed up in penv) + + for oblig in impl2_obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); + } + + if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ + {:?}", + impl1_trait_ref, + impl2_trait_ref, + errors, + infcx.parameter_environment.caller_bounds); + false + } else { + debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", + impl1_trait_ref, + impl2_trait_ref); + true + } +} diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs new file mode 100644 index 0000000000000..01f3b6333f84a --- /dev/null +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -0,0 +1,382 @@ +// Copyright 2016 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. + +use std::cell; +use std::rc::Rc; + +use super::{Overlap, specializes}; + +use middle::cstore::CrateStore; +use middle::def_id::DefId; +use middle::infer; +use middle::traits; +use middle::ty::{self, ImplOrTraitItem, TraitDef, TypeFoldable}; +use syntax::ast::Name; +use util::nodemap::DefIdMap; + +/// 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 +/// representing impls, and parent-child relationships representing +/// specializations. +/// +/// The graph provides two key services: +/// +/// - Construction, which implicitly checks for overlapping impls (i.e., impls +/// that overlap but where neither specializes the other -- an artifact of the +/// simple "chain" rule. +/// +/// - Parent extraction. In particular, the graph can give you the *immediate* +/// parents of a given specializing impl, which is needed for extracting +/// default items amongst other thigns. In the simple "chain" rule, every impl +/// has at most one parent. +pub struct Graph { + // all impls have a parent; the "root" impls have as their parent the def_id + // of the trait + parent: DefIdMap, + + // the "root" impls are found by looking up the trait's def_id. + children: DefIdMap>, +} + +impl Graph { + pub fn new() -> Graph { + Graph { + parent: Default::default(), + children: Default::default(), + } + } + + /// 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<'a, 'tcx>(&mut self, + tcx: &'a ty::ctxt<'tcx>, + impl_def_id: DefId) + -> Result<(), Overlap<'a, 'tcx>> { + 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; + + // 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 supress + // bogus errors). + if trait_ref.references_error() { + debug!("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(vec![]).push(impl_def_id); + return Ok(()); + } + + let mut parent = trait_def_id; + + // Ugly hack around borrowck limitations. Assigned only in the case + // where we bump downward an existing node in the graph. + let child_to_insert; + + 'descend: loop { + let mut possible_siblings = self.children.entry(parent).or_insert(vec![]); + + for slot in possible_siblings.iter_mut() { + let possible_sibling = *slot; + + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); + + if let Some(trait_ref) = overlap { + let le = specializes(tcx, impl_def_id, possible_sibling); + let ge = specializes(tcx, possible_sibling, impl_def_id); + + if le && !ge { + // the impl specializes possible_sibling + parent = possible_sibling; + continue 'descend; + } else if ge && !le { + // possible_sibling specializes the impl + *slot = impl_def_id; + self.parent.insert(possible_sibling, impl_def_id); + // we have to defer the insertion, because we can't + // relinquish the borrow of `self.children` + child_to_insert = possible_sibling; + break 'descend; + } else { + // overlap, but no specialization; error out + return Err(Overlap { + with_impl: possible_sibling, + on_trait_ref: trait_ref, + in_context: infcx, + }); + } + } + } + + // no overlap with any potential siblings, so add as a new sibling + self.parent.insert(impl_def_id, parent); + possible_siblings.push(impl_def_id); + return Ok(()); + } + + self.children.insert(impl_def_id, vec![child_to_insert]); + Ok(()) + } + + /// Insert cached metadata mapping from a child impl back to its parent. + pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) { + if self.parent.insert(child, parent).is_some() { + panic!("When recording an impl from the crate store, information about its parent \ + was already present."); + } + + self.children.entry(parent).or_insert(vec![]).push(child); + } + + /// 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() + } +} + +#[derive(Debug, Copy, Clone)] +/// A node in the specialization graph is either an impl or a trait +/// definition; either can serve as a source of item definitions. +/// There is always exactly one trait definition node: the root. +pub enum Node { + Impl(DefId), + Trait(DefId), +} + +impl Node { + pub fn is_from_trait(&self) -> bool { + match *self { + Node::Trait(..) => true, + _ => false, + } + } + + /// Iterate over the items defined directly by the given (impl or trait) node. + pub fn items<'a, 'tcx>(&self, tcx: &'a ty::ctxt<'tcx>) -> NodeItems<'a, 'tcx> { + match *self { + Node::Impl(impl_def_id) => { + NodeItems::Impl { + tcx: tcx, + items: cell::Ref::map(tcx.impl_items.borrow(), + |impl_items| &impl_items[&impl_def_id]), + idx: 0, + } + } + Node::Trait(trait_def_id) => { + NodeItems::Trait { + items: tcx.trait_items(trait_def_id).clone(), + idx: 0, + } + } + } + } + + pub fn def_id(&self) -> DefId { + match *self { + Node::Impl(did) => did, + Node::Trait(did) => did, + } + } +} + +/// An iterator over the items defined within a trait or impl. +pub enum NodeItems<'a, 'tcx: 'a> { + Impl { + tcx: &'a ty::ctxt<'tcx>, + items: cell::Ref<'a, Vec>, + idx: usize, + }, + Trait { + items: Rc>>, + idx: usize, + }, +} + +impl<'a, 'tcx> Iterator for NodeItems<'a, 'tcx> { + type Item = ImplOrTraitItem<'tcx>; + fn next(&mut self) -> Option> { + match *self { + NodeItems::Impl { tcx, ref items, ref mut idx } => { + let items_table = tcx.impl_or_trait_items.borrow(); + if *idx < items.len() { + let item_def_id = items[*idx].def_id(); + let item = items_table[&item_def_id].clone(); + *idx += 1; + Some(item) + } else { + None + } + } + NodeItems::Trait { ref items, ref mut idx } => { + if *idx < items.len() { + let item = items[*idx].clone(); + *idx += 1; + Some(item) + } else { + None + } + } + } + } +} + +pub struct Ancestors<'a, 'tcx: 'a> { + trait_def: &'a TraitDef<'tcx>, + current_source: Option, +} + +impl<'a, 'tcx> Iterator for Ancestors<'a, 'tcx> { + type Item = Node; + fn next(&mut self) -> Option { + let cur = self.current_source.take(); + if let Some(Node::Impl(cur_impl)) = cur { + let parent = self.trait_def.specialization_graph.borrow().parent(cur_impl); + if parent == self.trait_def.def_id() { + self.current_source = Some(Node::Trait(parent)); + } else { + self.current_source = Some(Node::Impl(parent)); + } + } + cur + } +} + +pub struct NodeItem { + pub node: Node, + pub item: T, +} + +impl NodeItem { + pub fn map U>(self, f: F) -> NodeItem { + NodeItem { + node: self.node, + item: f(self.item), + } + } +} + +pub struct TypeDefs<'a, 'tcx: 'a> { + // generally only invoked once or twice, so the box doesn't hurt + iter: Box>>> + 'a>, +} + +impl<'a, 'tcx> Iterator for TypeDefs<'a, 'tcx> { + type Item = NodeItem>>; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +pub struct FnDefs<'a, 'tcx: 'a> { + // generally only invoked once or twice, so the box doesn't hurt + iter: Box>>> + 'a>, +} + +impl<'a, 'tcx> Iterator for FnDefs<'a, 'tcx> { + type Item = NodeItem>>; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +pub struct ConstDefs<'a, 'tcx: 'a> { + // generally only invoked once or twice, so the box doesn't hurt + iter: Box>>> + 'a>, +} + +impl<'a, 'tcx> Iterator for ConstDefs<'a, 'tcx> { + type Item = NodeItem>>; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +impl<'a, 'tcx> Ancestors<'a, 'tcx> { + /// Seach the items from the given ancestors, returning each type definition + /// with the given name. + pub fn type_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> { + let iter = self.flat_map(move |node| { + node.items(tcx) + .filter_map(move |item| { + if let ty::TypeTraitItem(assoc_ty) = item { + if assoc_ty.name == name { + return Some(NodeItem { + node: node, + item: assoc_ty, + }); + } + } + None + }) + + }); + TypeDefs { iter: Box::new(iter) } + } + + /// Seach the items from the given ancestors, returning each fn definition + /// with the given name. + pub fn fn_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> { + let iter = self.flat_map(move |node| { + node.items(tcx) + .filter_map(move |item| { + if let ty::MethodTraitItem(method) = item { + if method.name == name { + return Some(NodeItem { + node: node, + item: method, + }); + } + } + None + }) + + }); + FnDefs { iter: Box::new(iter) } + } + + /// Seach the items from the given ancestors, returning each const + /// definition with the given name. + pub fn const_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> { + let iter = self.flat_map(move |node| { + node.items(tcx) + .filter_map(move |item| { + if let ty::ConstTraitItem(konst) = item { + if konst.name == name { + return Some(NodeItem { + node: node, + item: konst, + }); + } + } + None + }) + + }); + ConstDefs { iter: Box::new(iter) } + } +} + +/// Walk up the specialization ancestors of a given impl, starting with that +/// impl itself. +pub fn ancestors<'a, 'tcx>(trait_def: &'a TraitDef<'tcx>, + start_from_impl: DefId) + -> Ancestors<'a, 'tcx> { + Ancestors { + trait_def: trait_def, + current_source: Some(Node::Impl(start_from_impl)), + } +} diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index 85cea4d8096d6..c4e820bde8f57 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -10,7 +10,7 @@ use dep_graph::DepNode; use middle::def_id::DefId; -use middle::traits; +use middle::traits::{self, specialization_graph}; use middle::ty; use middle::ty::fast_reject; use middle::ty::{Ty, TyCtxt, TraitRef}; @@ -61,7 +61,7 @@ pub struct TraitDef<'tcx> { blanket_impls: RefCell>, /// The specialization order for impls of this trait. - pub specialization_graph: RefCell, + pub specialization_graph: RefCell, /// Various flags pub flags: Cell @@ -83,7 +83,7 @@ impl<'tcx> TraitDef<'tcx> { nonblanket_impls: RefCell::new(FnvHashMap()), blanket_impls: RefCell::new(vec![]), flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS), - specialization_graph: RefCell::new(traits::SpecializationGraph::new()), + specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()), } } @@ -170,7 +170,7 @@ impl<'tcx> TraitDef<'tcx> { /// Records a trait-to-implementation mapping for a non-local impl. /// /// The `parent_impl` is the immediately-less-specialized impl, or the - /// trait's def ID if the impl is maximally-specialized -- information that + /// trait's def ID if the impl is is not a specialization -- information that /// should be pulled from the metadata. pub fn record_remote_impl(&self, tcx: &TyCtxt<'tcx>, @@ -201,10 +201,8 @@ impl<'tcx> TraitDef<'tcx> { .insert(tcx, impl_def_id) } - /// Returns the immediately less specialized impl, if any. - pub fn parent_of_impl(&self, impl_def_id: DefId) -> Option { - let parent = self.specialization_graph.borrow().parent(impl_def_id); - if parent == self.trait_ref.def_id { None } else { Some(parent) } + pub fn ancestors<'a>(&'a self, of_impl: DefId) -> specialization_graph::Ancestors<'a, 'tcx> { + specialization_graph::ancestors(self, of_impl) } pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { diff --git a/src/librustc_data_structures/obligation_forest/README.md b/src/librustc_data_structures/obligation_forest/README.md index d76d7f6ba340e..982a2bacce164 100644 --- a/src/librustc_data_structures/obligation_forest/README.md +++ b/src/librustc_data_structures/obligation_forest/README.md @@ -60,7 +60,7 @@ which includes three bits of information: `process_obligations` would simply yield back further ambiguous results. This is used by the `FulfillmentContext` to decide when it has reached a steady state. - + #### Snapshots The `ObligationForest` supports a limited form of snapshots; see @@ -79,5 +79,3 @@ parent and (for convenience) its root (which may be itself). It also has a current state, described by `NodeState`. After each processing step, we compress the vector to remove completed and error nodes, which aren't needed anymore. - - diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index 939b527fad302..0b1418fc87845 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -1053,6 +1053,16 @@ pub enum Defaultness { Final, } +impl Defaultness { + pub fn is_final(&self) -> bool { + *self == Defaultness::Final + } + + pub fn is_default(&self) -> bool { + *self == Defaultness::Default + } +} + impl fmt::Display for Unsafety { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(match *self { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index bbe9d0ea4c609..707657cfa26bb 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1171,7 +1171,14 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, if let Some(trait_ref) = tcx.impl_trait_ref(did) { encode_trait_ref(rbml_w, ecx, trait_ref, tag_item_trait_ref); - let parent = tcx.lookup_trait_def(trait_ref.def_id).parent_of_impl(did); + let trait_def = tcx.lookup_trait_def(trait_ref.def_id); + let parent = trait_def.ancestors(did) + .skip(1) + .next() + .and_then(|node| match node { + specialization_graph::Node::Impl(parent) => Some(parent), + _ => None, + }); encode_parent_impl(rbml_w, parent); } encode_path(rbml_w, path.clone()); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 34ef4f4acec5e..dd144de6ec5e4 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -1138,7 +1138,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut selcx = traits::SelectionContext::new(&infcx); + let mut selcx = traits::build_selcx(&infcx).project_any().build(); let obligation = traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID), @@ -1199,7 +1199,7 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let tcx = ccx.tcx(); let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut selcx = traits::SelectionContext::new(&infcx); + let mut selcx = traits::build_selcx(&infcx).project_any().build(); let mut fulfill_cx = traits::FulfillmentContext::new(); let cause = traits::ObligationCause::dummy(); let traits::Normalized { value: predicates, obligations } = diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 072c1dfaa1d2a..2f5f052a72a85 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -486,21 +486,19 @@ pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, { assert!(!substs.types.needs_infer()); - traits::get_impl_item_or_default(tcx, impl_def_id, |cand| { - if let &ty::MethodTraitItem(ref meth) = cand { - if meth.name == name { - return Some(meth.clone()) + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + Some(node_item) => { + ImplMethod { + method: node_item.item, + substs: traits::translate_substs(tcx, impl_def_id, substs, node_item.node), + is_provided: node_item.node.is_from_trait(), } } - None - }).map(|(meth, source)| { - ImplMethod { - method: meth, - substs: source.translate_substs(tcx, substs), - is_provided: source.is_from_trait(), + None => { + tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id)) } - }).unwrap_or_else(|| { - tcx.sess.bug(&format!("method {:?} not found in {:?}", - name, impl_def_id)) - }) + } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 51b88612fe442..bb94d80474afb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -127,7 +127,7 @@ use syntax::util::lev_distance::find_best_match_for_name; use rustc_front::intravisit::{self, Visitor}; use rustc_front::hir; -use rustc_front::hir::{Visibility, PatKind, Defaultness}; +use rustc_front::hir::{Visibility, PatKind}; use rustc_front::print::pprust; use rustc_back::slice; @@ -864,33 +864,56 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env); } -fn check_specialization_validity<'tcx, F>(tcx: &ty::ctxt<'tcx>, - impl_id: DefId, - impl_item: &hir::ImplItem, - f: F) - where F: FnMut(&ty::ImplOrTraitItem<'tcx>) -> Option +fn report_forbidden_specialization(tcx: &ty::ctxt, + impl_item: &hir::ImplItem, + parent_impl: DefId) { - let parent_item_opt = traits::get_parent_impl_item(tcx, impl_id, f); - if let Some((Defaultness::Final, parent_impl)) = parent_item_opt { - let mut err = struct_span_err!( - tcx.sess, impl_item.span, E0520, - "item `{}` is provided by an implementation that \ - specializes another, but the item in the parent \ - implementations is not marked `default` and so it \ - cannot be specialized.", - impl_item.name); - - match tcx.span_of_impl(parent_impl) { - Ok(span) => { - err.span_note(span, "parent implementation is here:"); - } - Err(cname) => { - err.note(&format!("parent implementation is in crate `{}`", cname)); - } + let mut err = struct_span_err!( + tcx.sess, impl_item.span, E0520, + "item `{}` is provided by an implementation that specializes \ + another, but the item in the parent implementations is not \ + marked `default` and so it cannot be specialized.", + impl_item.name); + + match tcx.span_of_impl(parent_impl) { + Ok(span) => { + err.span_note(span, "parent implementation is here:"); } + Err(cname) => { + err.note(&format!("parent implementation is in crate `{}`", cname)); + } + } - err.emit(); + err.emit(); +} + +fn check_specialization_validity<'tcx>(tcx: &ty::ctxt<'tcx>, trait_def: &ty::TraitDef<'tcx>, + impl_id: DefId, impl_item: &hir::ImplItem) +{ + let ancestors = trait_def.ancestors(impl_id); + + let parent = match impl_item.node { + hir::ImplItemKind::Const(..) => { + ancestors.const_defs(tcx, impl_item.name).skip(1).next() + .map(|node_item| node_item.map(|parent| parent.defaultness)) + } + hir::ImplItemKind::Method(..) => { + ancestors.fn_defs(tcx, impl_item.name).skip(1).next() + .map(|node_item| node_item.map(|parent| parent.defaultness)) + + } + hir::ImplItemKind::Type(_) => { + ancestors.type_defs(tcx, impl_item.name).skip(1).next() + .map(|node_item| node_item.map(|parent| parent.defaultness)) + } + }; + + if let Some(parent) = parent { + if parent.item.is_final() { + report_forbidden_specialization(tcx, impl_item, parent.node.def_id()); + } } + } fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, @@ -898,8 +921,14 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_id: DefId, impl_trait_ref: &ty::TraitRef<'tcx>, impl_items: &[hir::ImplItem]) { - // Locate trait methods + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { return; } + + // Locate trait definition and items let tcx = ccx.tcx; + let trait_def = tcx.lookup_trait_def(impl_trait_ref.def_id); let trait_items = tcx.trait_items(impl_trait_ref.def_id); let mut overridden_associated_type = None; @@ -910,6 +939,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let ty_trait_item = trait_items.iter() .find(|ac| ac.name() == ty_impl_item.name()); + // Check that impl definition matches trait definition if let Some(ty_trait_item) = ty_trait_item { match impl_item.node { hir::ImplItemKind::Const(..) => { @@ -932,15 +962,6 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_const.name, impl_trait_ref) } - - check_specialization_validity(ccx.tcx, impl_id, impl_item, |cand| { - if let &ty::ConstTraitItem(ref trait_const) = cand { - if trait_const.name == impl_item.name { - return Some(trait_const.defaultness); - } - } - None - }); } hir::ImplItemKind::Method(ref sig, ref body) => { check_trait_fn_not_const(ccx, impl_item.span, sig.constness); @@ -964,15 +985,6 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_method.name, impl_trait_ref) } - - check_specialization_validity(ccx.tcx, impl_id, impl_item, |cand| { - if let &ty::MethodTraitItem(ref meth) = cand { - if meth.name == impl_method.name { - return Some(meth.defaultness); - } - } - None - }); } hir::ImplItemKind::Type(_) => { let impl_type = match ty_impl_item { @@ -991,18 +1003,11 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_type.name, impl_trait_ref) } - - check_specialization_validity(ccx.tcx, impl_id, impl_item, |cand| { - if let &ty::TypeTraitItem(ref at) = cand { - if at.name == impl_item.name { - return Some(at.defaultness); - } - } - None - }); } } } + + check_specialization_validity(tcx, trait_def, impl_id, impl_item); } // Check for missing items from trait @@ -1011,9 +1016,13 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let mut invalidated_items = Vec::new(); let associated_type_overridden = overridden_associated_type.is_some(); for trait_item in trait_items.iter() { + let is_implemented; + let is_provided; + match *trait_item { ty::ConstTraitItem(ref associated_const) => { - let is_implemented = impl_items.iter().any(|ii| { + is_provided = associated_const.has_value; + is_implemented = impl_items.iter().any(|ii| { match ii.node { hir::ImplItemKind::Const(..) => { ii.name == associated_const.name @@ -1021,57 +1030,30 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, _ => false, } }); - let is_provided = associated_const.has_value; - - if !is_implemented { - if !is_provided { - missing_items.push(associated_const.name); - } else if associated_type_overridden { - invalidated_items.push(associated_const.name); - } - } } ty::MethodTraitItem(ref trait_method) => { - let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| { - if let &ty::MethodTraitItem(ref meth) = cand { - if meth.name == trait_method.name { - return Some(()); - } - } - None - }); - - if let Some((_, source)) = search_result { - if source.is_from_trait() { - let is_provided = - provided_methods.iter().any(|m| m.name == trait_method.name); - if !is_provided { - missing_items.push(trait_method.name); - } else if associated_type_overridden { - invalidated_items.push(trait_method.name); - } - } - } else { - missing_items.push(trait_method.name); - } + is_provided = provided_methods.iter().any(|m| m.name == trait_method.name); + is_implemented = trait_def.ancestors(impl_id) + .fn_defs(tcx, trait_method.name) + .next() + .map(|node_item| !node_item.node.is_from_trait()) + .unwrap_or(false); } ty::TypeTraitItem(ref trait_assoc_ty) => { - let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| { - if let &ty::TypeTraitItem(ref assoc_ty) = cand { - if assoc_ty.name == trait_assoc_ty.name && assoc_ty.ty.is_some() { - return Some(()); - } - } - None - }); + is_provided = trait_assoc_ty.ty.is_some(); + is_implemented = trait_def.ancestors(impl_id) + .type_defs(tcx, trait_assoc_ty.name) + .next() + .map(|node_item| !node_item.node.is_from_trait()) + .unwrap_or(false); + } + } - if let Some((_, source)) = search_result { - if source.is_from_trait() && associated_type_overridden { - invalidated_items.push(trait_assoc_ty.name); - } - } else { - missing_items.push(trait_assoc_ty.name); - } + if !is_implemented { + if !is_provided { + missing_items.push(trait_item.name()); + } else if associated_type_overridden { + invalidated_items.push(trait_item.name()); } } } diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 1ebc131224dc7..4a5dbe20b7879 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -128,7 +128,8 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; - let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id)); + let _task = self.tcx.dep_graph.in_task( + DepNode::CoherenceOverlapCheck(trait_def_id)); let def = self.tcx.lookup_trait_def(trait_def_id); @@ -161,7 +162,8 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { err.span_note(span, "conflicting implementation is here:"); } Err(cname) => { - err.note(&format!("conflicting implementation in crate `{}`", cname)); + err.note(&format!("conflicting implementation in crate `{}`", + cname)); } } diff --git a/src/test/compile-fail/specialization-no-default.rs b/src/test/compile-fail/specialization-no-default.rs index 06a01f0ca0575..88cd00e69c422 100644 --- a/src/test/compile-fail/specialization-no-default.rs +++ b/src/test/compile-fail/specialization-no-default.rs @@ -65,7 +65,7 @@ impl Baz for T { } impl Baz for i32 { - fn baz(&self) {} + fn baz(&self) {} //~ ERROR E0520 } //////////////////////////////////////////////////////////////////////////////// @@ -86,7 +86,7 @@ impl Redundant for T { } impl Redundant for i32 { - default fn redundant(&self) {} + default fn redundant(&self) {} //~ ERROR E0520 } fn main() {} From 8f20cbf03088c8fa89a343de9e97e7608cd701b6 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 17 Feb 2016 09:04:26 -0800 Subject: [PATCH 23/37] Add more commentary for subst translation --- src/librustc/middle/traits/specialize/mod.rs | 32 ++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index 2dc4926736e60..9350598763375 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -64,9 +64,35 @@ pub fn translate_substs<'tcx>(tcx: &ty::ctxt<'tcx>, /// When we have selected one impl, but are actually using item definitions from /// a parent impl providing a default, we need a way to translate between the /// type parameters of the two impls. Here the `source_impl` is the one we've -/// selected, and `source_substs` is a substitution of its generics (and possibly -/// some relevant `FnSpace` variables as well). And `target_impl` is the impl -/// we're actually going to get the definition from. +/// selected, and `source_substs` is a substitution of its generics (and +/// possibly some relevant `FnSpace` variables as well). And `target_impl` is +/// the impl we're actually going to get the definition from. The resulting +/// substitution will map from `target_impl`'s generics to `source_impl`'s +/// generics as instantiated by `source_subst`. +/// +/// For example, consider the following scenario: +/// +/// ```rust +/// trait Foo { ... } +/// impl Foo for (T, U) { ... } // target impl +/// impl Foo for (V, V) { ... } // source impl +/// ``` +/// +/// Suppose we have selected "source impl" with `V` instantiated with `u32`. +/// This function will produce a substitution with `T` and `U` both mapping to `u32`. +/// +/// Where clauses add some trickiness here, because they can be used to "define" +/// an argument indirectly: +/// +/// ```rust +/// impl<'a, I, T: 'a> Iterator for Cloned +/// where I: Iterator, T: Clone +/// ``` +/// +/// In a case like this, the substitution for `T` is determined indirectly, +/// through associated type projection. We deal with such cases by using +/// *fulfillment* to relate the two impls, requiring that all projections are +/// resolved. fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>, source_impl: DefId, source_substs: Substs<'tcx>, From eaf2f909561c3b2c26ee8d40ac7a95e89c5034d9 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 19 Feb 2016 10:49:14 -0800 Subject: [PATCH 24/37] Refactor core specialization and subst translation code to avoid danger of inference variables floating around without their inference context. The main insight here is that, when we are translating substitutions between two impls, *we already know that the more specific impl holds*, so we do not need to add its obligations to the parameter environment. Instead, we can just thread through the inference context we used to show select the more specific impl in the first place. --- src/librustc/middle/traits/project.rs | 2 +- src/librustc/middle/traits/specialize/mod.rs | 312 ++++++++---------- .../traits/specialize/specialization_graph.rs | 1 + src/librustc_trans/trans/collector.rs | 14 +- src/librustc_trans/trans/meth.rs | 5 +- 5 files changed, 156 insertions(+), 178 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 20d6c0b6fc038..2df3184a0fba1 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -1008,7 +1008,7 @@ fn confirm_impl_candidate<'cx,'tcx>( obligation.predicate.trait_ref); tcx.types.err }); - let substs = translate_substs(tcx, impl_def_id, substs, node_item.node); + let substs = translate_substs(selcx.infcx(), impl_def_id, substs, node_item.node); (ty.subst(tcx, &substs), nested) } None => { diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index 9350598763375..5939128719c01 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -17,7 +17,8 @@ // See traits/README.md for a bit more detail on how specialization // fits together with the rest of the trait machinery. -use super::{util, build_selcx, SelectionContext}; +use super::{build_selcx, SelectionContext, FulfillmentContext}; +use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig}; use middle::cstore::CrateStore; use middle::def_id::DefId; @@ -40,117 +41,90 @@ pub struct Overlap<'a, 'tcx: 'a> { /// Given a subst for the requested impl, translate it to a subst /// appropriate for the actual item definition (whether it be in that impl, /// a parent impl, or the trait). -pub fn translate_substs<'tcx>(tcx: &ty::ctxt<'tcx>, - from_impl: DefId, - from_impl_substs: Substs<'tcx>, - to_node: specialization_graph::Node) - -> Substs<'tcx> { - match to_node { - specialization_graph::Node::Impl(to_impl) => { +// When we have selected one impl, but are actually using item definitions from +// a parent impl providing a default, we need a way to translate between the +// type parameters of the two impls. Here the `source_impl` is the one we've +// selected, and `source_substs` is a substitution of its generics (and +// possibly some relevant `FnSpace` variables as well). And `target_node` is +// the impl/trait we're actually going to get the definition from. The resulting +// substitution will map from `target_node`'s generics to `source_impl`'s +// generics as instantiated by `source_subst`. +// +// For example, consider the following scenario: +// +// ```rust +// trait Foo { ... } +// impl Foo for (T, U) { ... } // target impl +// impl Foo for (V, V) { ... } // source impl +// ``` +// +// Suppose we have selected "source impl" with `V` instantiated with `u32`. +// This function will produce a substitution with `T` and `U` both mapping to `u32`. +// +// Where clauses add some trickiness here, because they can be used to "define" +// an argument indirectly: +// +// ```rust +// impl<'a, I, T: 'a> Iterator for Cloned +// where I: Iterator, T: Clone +// ``` +// +// In a case like this, the substitution for `T` is determined indirectly, +// through associated type projection. We deal with such cases by using +// *fulfillment* to relate the two impls, requiring that all projections are +// resolved. +pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>, + target_node: specialization_graph::Node) + -> Substs<'tcx> +{ + let source_trait_ref = infcx.tcx + .impl_trait_ref(source_impl) + .unwrap() + .subst(infcx.tcx, &source_substs); + + // translate the Self and TyParam parts of the substitution, since those + // vary across impls + let target_substs = match target_node { + specialization_graph::Node::Impl(target_impl) => { // no need to translate if we're targetting the impl we started with - if from_impl == to_impl { - return from_impl_substs; + if source_impl == target_impl { + return source_substs; } - translate_substs_between_impls(tcx, from_impl, from_impl_substs, to_impl) - - } - specialization_graph::Node::Trait(..) => { - translate_substs_from_impl_to_trait(tcx, from_impl, from_impl_substs) + fulfill_implication(infcx, source_trait_ref, target_impl).unwrap_or_else(|_| { + infcx.tcx + .sess + .bug("When translating substitutions for specialization, the expected \ + specializaiton failed to hold") + }) } - } -} - -/// When we have selected one impl, but are actually using item definitions from -/// a parent impl providing a default, we need a way to translate between the -/// type parameters of the two impls. Here the `source_impl` is the one we've -/// selected, and `source_substs` is a substitution of its generics (and -/// possibly some relevant `FnSpace` variables as well). And `target_impl` is -/// the impl we're actually going to get the definition from. The resulting -/// substitution will map from `target_impl`'s generics to `source_impl`'s -/// generics as instantiated by `source_subst`. -/// -/// For example, consider the following scenario: -/// -/// ```rust -/// trait Foo { ... } -/// impl Foo for (T, U) { ... } // target impl -/// impl Foo for (V, V) { ... } // source impl -/// ``` -/// -/// Suppose we have selected "source impl" with `V` instantiated with `u32`. -/// This function will produce a substitution with `T` and `U` both mapping to `u32`. -/// -/// Where clauses add some trickiness here, because they can be used to "define" -/// an argument indirectly: -/// -/// ```rust -/// impl<'a, I, T: 'a> Iterator for Cloned -/// where I: Iterator, T: Clone -/// ``` -/// -/// In a case like this, the substitution for `T` is determined indirectly, -/// through associated type projection. We deal with such cases by using -/// *fulfillment* to relate the two impls, requiring that all projections are -/// resolved. -fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>, - source_impl: DefId, - source_substs: Substs<'tcx>, - target_impl: DefId) - -> Substs<'tcx> { - - // We need to build a subst that covers all the generics of - // `target_impl`. Start by introducing fresh infer variables: - let target_generics = tcx.lookup_item_type(target_impl).generics; - let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut target_substs = infcx.fresh_substs_for_generics(DUMMY_SP, &target_generics); - if source_substs.regions.is_erased() { - target_substs = target_substs.erase_regions() - } + specialization_graph::Node::Trait(..) => source_trait_ref.substs.clone(), + }; - if !fulfill_implication(&mut infcx, - source_impl, - source_substs.clone(), - target_impl, - target_substs.clone()) { - tcx.sess - .bug("When translating substitutions for specialization, the expected specializaiton \ - failed to hold") - } + // retain erasure mode + // NB: this must happen before inheriting method generics below + let target_substs = if source_substs.regions.is_erased() { + target_substs.erase_regions() + } else { + target_substs + }; - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. We - // also carry along any FnSpace substitutions, which don't need to be - // adjusted when mapping from one impl to another. - infcx.resolve_type_vars_if_possible(&target_substs) - .with_method_from_subst(&source_substs) + // directly inherent the method generics, since those do not vary across impls + target_substs.with_method_from_subst(&source_substs) } -/// When we've selected an impl but need to use an item definition provided by -/// the trait itself, we need to translate the substitution applied to the impl -/// to one that makes sense for the trait. -fn translate_substs_from_impl_to_trait<'tcx>(tcx: &ty::ctxt<'tcx>, - source_impl: DefId, - source_substs: Substs<'tcx>) - -> Substs<'tcx> { - let source_trait_ref = tcx.impl_trait_ref(source_impl).unwrap().subst(tcx, &source_substs); +fn skolemizing_subst_for_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + impl_def_id: DefId) + -> Substs<'tcx> +{ + let impl_generics = infcx.tcx.lookup_item_type(impl_def_id).generics; - let mut new_substs = source_trait_ref.substs.clone(); - if source_substs.regions.is_erased() { - new_substs = new_substs.erase_regions() - } - - // Carry any FnSpace substitutions along; they don't need to be adjusted - new_substs.with_method_from_subst(&source_substs) -} + let types = impl_generics.types.map(|def| infcx.tcx.mk_param_from_def(def)); -fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { - let impl_generics = tcx.lookup_item_type(impl_def_id).generics; - - let types = impl_generics.types.map(|def| tcx.mk_param_from_def(def)); - - // FIXME: figure out what we actually want here + // TODO: figure out what we actually want here let regions = impl_generics.regions.map(|_| ty::Region::ReStatic); // |d| infcx.next_region_var(infer::RegionVariableOrigin::EarlyBoundRegion(span, d.name))); @@ -170,101 +144,101 @@ pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> // We determine whether there's a subset relationship by: // // - skolemizing impl1, - // - instantiating impl2 with fresh inference variables, // - assuming the where clauses for impl1, + // - instantiating impl2 with fresh inference variables, // - unifying, // - attempting to prove the where clauses for impl2 // - // The last three steps are essentially checking for an implication between two impls - // after appropriate substitutions. This is what `fulfill_implication` checks for. + // The last three steps are encapsulated in `fulfill_implication`. // // See RFC 1210 for more details and justification. let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id); - let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); - - fulfill_implication(&mut infcx, - impl1_def_id, - impl1_substs, - impl2_def_id, - impl2_substs) -} - -/// Does impl1 (instantiated with the impl1_substs) imply impl2 -/// (instantiated with impl2_substs)? -/// -/// Mutates the `infcx` in two ways: -/// - by adding the obligations of impl1 to the parameter environment -/// - via fulfillment, so that if the implication holds the various unifications -fn fulfill_implication<'a, 'tcx>(infcx: &mut InferCtxt<'a, 'tcx>, - impl1_def_id: DefId, - impl1_substs: Substs<'tcx>, - impl2_def_id: DefId, - impl2_substs: Substs<'tcx>) - -> bool { - let tcx = &infcx.tcx; - + // Skiolemize impl1: we want to prove that "for all types matched by impl1, + // those types are also matched by impl2". + let impl1_substs = skolemizing_subst_for_impl(&infcx, impl1_def_id); let (impl1_trait_ref, impl1_obligations) = { let selcx = &mut SelectionContext::new(&infcx); - util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) + impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) }; + // Add impl1's obligations as assumptions to the environment. let impl1_predicates: Vec<_> = impl1_obligations.iter() .cloned() .map(|oblig| oblig.predicate) .collect(); - infcx.parameter_environment = ty::ParameterEnvironment { tcx: tcx, free_substs: impl1_substs, - implicit_region_bound: ty::ReEmpty, // FIXME: is this OK? + implicit_region_bound: ty::ReEmpty, // TODO: is this OK? caller_bounds: impl1_predicates, selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), - free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK? + free_id_outlive: region::DUMMY_CODE_EXTENT, // TODO: is this OK? }; - let selcx = &mut build_selcx(&infcx).project_topmost().build(); - let (impl2_trait_ref, impl2_obligations) = util::impl_trait_ref_and_oblig(selcx, - impl2_def_id, - &impl2_substs); - - // do the impls unify? If not, no specialization. - if let Err(_) = infer::mk_eq_trait_refs(&infcx, - true, - TypeOrigin::Misc(DUMMY_SP), - impl1_trait_ref, - impl2_trait_ref) { - debug!("fulfill_implication: {:?} does not unify with {:?}", - impl1_trait_ref, - impl2_trait_ref); - return false; - } + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok() +} - let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); +/// Attempt to fulfill all obligations of `target_impl` after unification with +/// `source_trait_ref`. If successful, returns a substitution for *all* the +/// generics of `target_impl`, including both those needed to unify with +/// `source_trait_ref` and those whose identity is determined via a where +/// clause in the impl. +fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + source_trait_ref: ty::TraitRef<'tcx>, + target_impl: DefId) + -> Result, ()> +{ + infcx.probe(|_| { + let selcx = &mut build_selcx(&infcx).project_topmost().build(); + let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl); + let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx, + target_impl, + &target_substs); + + // do the impls unify? If not, no specialization. + if let Err(_) = infer::mk_eq_trait_refs(&infcx, + true, + TypeOrigin::Misc(DUMMY_SP), + source_trait_ref, + target_trait_ref) { + debug!("fulfill_implication: {:?} does not unify with {:?}", + source_trait_ref, + target_trait_ref); + return Err(()); + } - // attempt to prove all of the predicates for impl2 given those for impl1 - // (which are packed up in penv) + // attempt to prove all of the predicates for impl2 given those for impl1 + // (which are packed up in penv) - for oblig in impl2_obligations.into_iter() { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } + let mut fulfill_cx = FulfillmentContext::new(); + for oblig in obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); + } - if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { - // no dice! - debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ - {:?}", - impl1_trait_ref, - impl2_trait_ref, - errors, - infcx.parameter_environment.caller_bounds); - false - } else { - debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", - impl1_trait_ref, - impl2_trait_ref); - true - } + if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} \ + given {:?}", + source_trait_ref, + target_trait_ref, + errors, + infcx.parameter_environment.caller_bounds); + Err(()) + } else { + debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses \ + elided)", + source_trait_ref, + target_trait_ref); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + + // TODO: should this use `fully_resolve` instead? + Ok(infcx.resolve_type_vars_if_possible(&target_substs)) + } + }) } diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs index 01f3b6333f84a..411c98c6066cf 100644 --- a/src/librustc/middle/traits/specialize/specialization_graph.rs +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -105,6 +105,7 @@ impl Graph { } else if ge && !le { // possible_sibling specializes the impl *slot = impl_def_id; + self.parent.insert(impl_def_id, parent); self.parent.insert(possible_sibling, impl_def_id); // we have to defer the insertion, because we can't // relinquish the borrow of `self.children` diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs index abfd127f38860..b7e107ae5e783 100644 --- a/src/librustc_trans/trans/collector.rs +++ b/src/librustc_trans/trans/collector.rs @@ -819,9 +819,10 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, nested: _ }) => { let callee_substs = impl_substs.with_method_from(&rcvr_substs); - let impl_method = tcx.get_impl_method(impl_did, - tcx.mk_substs(callee_substs), - trait_method.name); + let impl_method = meth::get_impl_method(tcx, + impl_did, + tcx.mk_substs(callee_substs), + trait_method.name); Some((impl_method.method.def_id, impl_method.substs)) } // If we have a closure or a function pointer, we will also encounter @@ -1160,9 +1161,10 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let mth = tcx.get_impl_method(impl_def_id, - callee_substs, - default_impl.name); + let mth = meth::get_impl_method(tcx, + impl_def_id, + callee_substs.clone(), + default_impl.name); assert!(mth.is_provided); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 2f5f052a72a85..28e9fcd47ff02 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -381,7 +381,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, impl_id: DefId, substs: &'tcx subst::Substs<'tcx>) - -> Vec>> + -> Vec>> { let tcx = ccx.tcx(); @@ -488,12 +488,13 @@ pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { Some(node_item) => { ImplMethod { method: node_item.item, - substs: traits::translate_substs(tcx, impl_def_id, substs, node_item.node), + substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node), is_provided: node_item.node.is_from_trait(), } } From 1726c1b54a12b9f499affdc3ef0b769f3de15a23 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 22 Feb 2016 22:21:37 -0800 Subject: [PATCH 25/37] Add some debugging output for specialization graph assembly --- .../middle/traits/specialize/specialization_graph.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs index 411c98c6066cf..c73219fbc0882 100644 --- a/src/librustc/middle/traits/specialize/specialization_graph.rs +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -65,6 +65,8 @@ impl Graph { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; + debug!("inserting TraitRef {:?} into specialization graph", 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 supress @@ -99,10 +101,16 @@ impl Graph { let ge = specializes(tcx, possible_sibling, impl_def_id); if le && !ge { + let parent_trait_ref = tcx.impl_trait_ref(possible_sibling).unwrap(); + debug!("descending as child of TraitRef {:?}", parent_trait_ref); + // the impl specializes possible_sibling parent = possible_sibling; continue 'descend; } else if ge && !le { + let child_trait_ref = tcx.impl_trait_ref(possible_sibling).unwrap(); + debug!("placing as parent of TraitRef {:?}", child_trait_ref); + // possible_sibling specializes the impl *slot = impl_def_id; self.parent.insert(impl_def_id, parent); @@ -123,6 +131,7 @@ impl Graph { } // no overlap with any potential siblings, so add as a new sibling + debug!("placing as new sibling"); self.parent.insert(impl_def_id, parent); possible_siblings.push(impl_def_id); return Ok(()); From 2651f8c14748bf4598841471329ca260f5860ec0 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 22 Feb 2016 22:46:45 -0800 Subject: [PATCH 26/37] Add regression tests from initial implementation review --- .../run-pass/specialization-out-of-order.rs | 27 ++++++++++++++++++ .../specialization-projection-alias.rs | 28 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/test/run-pass/specialization-out-of-order.rs create mode 100644 src/test/run-pass/specialization-projection-alias.rs diff --git a/src/test/run-pass/specialization-out-of-order.rs b/src/test/run-pass/specialization-out-of-order.rs new file mode 100644 index 0000000000000..2d293f494a347 --- /dev/null +++ b/src/test/run-pass/specialization-out-of-order.rs @@ -0,0 +1,27 @@ +// Copyright 2016 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. + +// Test that you can list the more specific impl before the more general one. + +#![feature(specialization)] + +trait Foo { + type Out; +} + +impl Foo for bool { + type Out = (); +} + +impl Foo for T { + default type Out = bool; +} + +fn main() {} diff --git a/src/test/run-pass/specialization-projection-alias.rs b/src/test/run-pass/specialization-projection-alias.rs new file mode 100644 index 0000000000000..2250d77e08e8e --- /dev/null +++ b/src/test/run-pass/specialization-projection-alias.rs @@ -0,0 +1,28 @@ +// Copyright 2016 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)] + +// Regression test for ICE when combining specialized associated types and type +// aliases + +trait Id_ { + type Out; +} + +type Id = ::Out; + +impl Id_ for T { + default type Out = T; +} + +fn main() { + let x: Id = panic!(); +} From 386f8eefc0a0775b228740cc5f682b6d10050e04 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 23 Feb 2016 07:48:34 -0800 Subject: [PATCH 27/37] Forbid cross-polarity specializations --- src/librustc/middle/traits/specialize/mod.rs | 6 +++++ ...ive-impl.rs => specialization-polarity.rs} | 22 +++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) rename src/test/compile-fail/{specialization-negative-impl.rs => specialization-polarity.rs} (66%) mode change 100644 => 100755 diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index 5939128719c01..6fd61741268b6 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -41,6 +41,7 @@ pub struct Overlap<'a, 'tcx: 'a> { /// Given a subst for the requested impl, translate it to a subst /// appropriate for the actual item definition (whether it be in that impl, /// a parent impl, or the trait). +// // When we have selected one impl, but are actually using item definitions from // a parent impl providing a default, we need a way to translate between the // type parameters of the two impls. Here the `source_impl` is the one we've @@ -153,6 +154,11 @@ pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> // // See RFC 1210 for more details and justification. + // Currently we do not allow e.g. a negative impl to specialize a positive one + if tcx.trait_impl_polarity(impl1_def_id) != tcx.trait_impl_polarity(impl2_def_id) { + return false + } + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); // Skiolemize impl1: we want to prove that "for all types matched by impl1, diff --git a/src/test/compile-fail/specialization-negative-impl.rs b/src/test/compile-fail/specialization-polarity.rs old mode 100644 new mode 100755 similarity index 66% rename from src/test/compile-fail/specialization-negative-impl.rs rename to src/test/compile-fail/specialization-polarity.rs index 9aec614012e4d..27a3e31491b82 --- a/src/test/compile-fail/specialization-negative-impl.rs +++ b/src/test/compile-fail/specialization-polarity.rs @@ -8,19 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Make sure specialization cannot change impl polarity + #![feature(optin_builtin_traits)] #![feature(specialization)] -struct TestType(T); +trait Foo {} + +impl Foo for .. {} + +impl Foo for T {} +impl !Foo for u8 {} //~ ERROR E0119 -// TODO: nail down the rules here with @nikomatsakis +trait Bar {} -unsafe impl Send for TestType {} -impl !Send for TestType {} +impl Bar for .. {} -fn assert_send() {} +impl !Bar for T {} +impl Bar for u8 {} //~ ERROR E0119 -fn main() { - assert_send::>(); - assert_send::>(); //~ ERROR -} +fn main() {} From 9bcfdb7b9c5c37a38b1b5c71a5efa14f037d0c74 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 23 Feb 2016 12:47:09 -0800 Subject: [PATCH 28/37] Move projection_mode to InferContext rather than SelectionContext to reduce chance of bugs --- src/librustc/middle/check_match.rs | 7 +- src/librustc/middle/const_eval.rs | 8 +- src/librustc/middle/infer/mod.rs | 23 ++- src/librustc/middle/traits/coherence.rs | 3 +- src/librustc/middle/traits/mod.rs | 10 +- src/librustc/middle/traits/project.rs | 88 ++++++++++++ src/librustc/middle/traits/select.rs | 136 ++---------------- src/librustc/middle/traits/specialize/mod.rs | 8 +- .../traits/specialize/specialization_graph.rs | 4 +- src/librustc/middle/ty/util.rs | 12 +- src/librustc_borrowck/borrowck/check_loans.rs | 6 +- .../borrowck/gather_loans/mod.rs | 11 +- src/librustc_lint/builtin.rs | 6 +- src/librustc_mir/mir_map.rs | 8 +- src/librustc_passes/consts.rs | 12 +- src/librustc_passes/rvalues.rs | 4 +- src/librustc_trans/trans/_match.rs | 5 +- src/librustc_trans/trans/attributes.rs | 5 +- src/librustc_trans/trans/closure.rs | 5 +- src/librustc_trans/trans/common.rs | 10 +- src/librustc_trans/trans/declare.rs | 5 +- src/librustc_trans/trans/meth.rs | 4 +- src/librustc_typeck/check/compare_method.rs | 6 +- src/librustc_typeck/check/dropck.rs | 7 +- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/coherence/mod.rs | 9 +- src/librustc_typeck/lib.rs | 3 +- 27 files changed, 225 insertions(+), 184 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 51985be96db53..89b57e0d90a00 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -25,6 +25,7 @@ use middle::expr_use_visitor as euv; use middle::infer; use middle::mem_categorization::{cmt}; use middle::pat_util::*; +use middle::traits::ProjectionMode; use middle::ty::*; use middle::ty; use std::cmp::Ordering; @@ -1101,7 +1102,8 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, //FIXME: (@jroesch) this code should be floated up as well let infcx = infer::new_infer_ctxt(cx.tcx, &cx.tcx.tables, - Some(cx.param_env.clone())); + Some(cx.param_env.clone()), + ProjectionMode::AnyFinal); if infcx.type_moves_by_default(pat_ty, pat.span) { check_move(p, sub.as_ref().map(|p| &**p)); } @@ -1133,7 +1135,8 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>, let infcx = infer::new_infer_ctxt(cx.tcx, &cx.tcx.tables, - Some(checker.cx.param_env.clone())); + Some(checker.cx.param_env.clone()), + ProjectionMode::AnyFinal); let mut visitor = ExprUseVisitor::new(&mut checker, &infcx); visitor.walk_expr(guard); diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index d97df585edc05..8a1a0080eb0c2 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -24,6 +24,7 @@ use middle::def_id::DefId; use middle::pat_util::def_to_path; use middle::ty::{self, Ty, TyCtxt}; use middle::ty::util::IntTypeExt; +use middle::traits::ProjectionMode; use middle::astconv_util::ast_ty_to_prim_ty; use util::nodemap::NodeMap; @@ -1049,7 +1050,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, trait_ref); tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), @@ -1067,6 +1068,11 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, } }; + // NOTE: this code does not currently account for specialization, but when + // it does so, it should hook into the ProjectionMode to determine when the + // constant should resolve; this will also require plumbing through to this + // function whether we are in "trans mode" to pick the right ProjectionMode + // when constructing the inference context above. match selection { traits::VtableImpl(ref impl_data) => { match tcx.associated_consts(impl_data.impl_def_id) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index e08167e7ce688..4ff1de422117b 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -27,7 +27,7 @@ use middle::region::CodeExtent; use middle::subst; use middle::subst::Substs; use middle::subst::Subst; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use middle::ty::adjustment; use middle::ty::{TyVid, IntVid, FloatVid}; use middle::ty::{self, Ty, TyCtxt}; @@ -99,6 +99,11 @@ pub struct InferCtxt<'a, 'tcx: 'a> { // directly. normalize: bool, + // Sadly, the behavior of projection varies a bit depending on the + // stage of compilation. The specifics are given in the + // documentation for `ProjectionMode`. + projection_mode: ProjectionMode, + err_count_on_creation: usize, } @@ -354,7 +359,8 @@ pub fn fixup_err_to_string(f: FixupError) -> String { pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, tables: &'a RefCell>, - param_env: Option>) + param_env: Option>, + projection_mode: ProjectionMode) -> InferCtxt<'a, 'tcx> { InferCtxt { tcx: tcx, @@ -366,14 +372,16 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()), reported_trait_errors: RefCell::new(FnvHashSet()), normalize: false, + projection_mode: projection_mode, err_count_on_creation: tcx.sess.err_count() } } pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, - tables: &'a RefCell>) + tables: &'a RefCell>, + projection_mode: ProjectionMode) -> InferCtxt<'a, 'tcx> { - let mut infcx = new_infer_ctxt(tcx, tables, None); + let mut infcx = new_infer_ctxt(tcx, tables, None, projection_mode); infcx.normalize = true; infcx } @@ -514,6 +522,7 @@ pub struct CombinedSnapshot { region_vars_snapshot: RegionSnapshot, } +// NOTE: Callable from trans only! pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T where T : TypeFoldable<'tcx> { @@ -525,7 +534,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T return value; } - let infcx = new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Any); let mut selcx = traits::SelectionContext::new(&infcx); let cause = traits::ObligationCause::dummy(); let traits::Normalized { value: result, obligations } = @@ -593,6 +602,10 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + pub fn projection_mode(&self) -> ProjectionMode { + self.projection_mode + } + pub fn freshen>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index bc3d3e48d6b1a..33a1e3816e348 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -10,7 +10,6 @@ //! See `README.md` for high-level documentation -use super::build_selcx; use super::{SelectionContext, Obligation, ObligationCause}; use super::util; @@ -37,7 +36,7 @@ pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>, impl1_def_id, impl2_def_id); - let selcx = &mut build_selcx(infcx).project_topmost().intercrate().build(); + let selcx = &mut SelectionContext::intercrate(infcx); overlap(selcx, impl1_def_id, impl2_def_id) } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index d9fc53ff2c261..76dbd2793f9f6 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -36,17 +36,15 @@ pub use self::coherence::orphan_check; pub use self::coherence::overlapping_impls; pub use self::coherence::OrphanCheckErr; pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation}; -pub use self::project::MismatchedProjectionTypes; -pub use self::project::normalize; -pub use self::project::Normalized; +pub use self::project::{MismatchedProjectionTypes, ProjectionMode}; +pub use self::project::{normalize, Normalized}; pub use self::object_safety::is_object_safe; pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::object_safety_violations; pub use self::object_safety::ObjectSafetyViolation; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::is_vtable_safe_method; -pub use self::select::{EvaluationCache, SelectionContextBuilder, build_selcx}; -pub use self::select::{ProjectionMode, SelectionContext, SelectionCache}; +pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs}; @@ -435,7 +433,7 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi let elaborated_env = unnormalized_env.with_caller_bounds(predicates); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env)); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env), ProjectionMode::AnyFinal); let predicates = match fully_normalize(&infcx, cause, &infcx.parameter_environment.caller_bounds) { diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 2df3184a0fba1..58d36b45ecb0a 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -35,6 +35,94 @@ use util::common::FN_OUTPUT_NAME; use std::rc::Rc; +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ProjectionMode { + /// At coherence-checking time, we're still constructing the + /// specialization graph, and thus we only project project + /// non-`default` associated types that are defined directly in + /// the applicable impl. (This behavior should be improved over + /// time, to allow for successful projections modulo cycles + /// between different impls). + // TODO: Add tracking issue to do better here. + /// + /// Here's an example that will fail due to the restriction: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// type Output = bool; + /// } + /// + /// impl Assoc for u8 {} // <- inherits the non-default type from above + /// + /// trait Foo {} + /// impl Foo for u32 {} + /// impl Foo for ::Output {} // <- this projection will fail + /// ``` + /// + /// The projection would succeed if `Output` had been defined + /// directly in the impl for `u8`. + // TODO: Add test + Topmost, + + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let <() as Assoc>::Output = true; + /// } + // TODO: Add test + AnyFinal, + + /// At trans time, all projections will succeed. + Any, +} + +impl ProjectionMode { + pub fn topmost(&self) -> bool { + match *self { + ProjectionMode::Topmost => true, + _ => false, + } + } + + pub fn any_final(&self) -> bool { + match *self { + ProjectionMode::AnyFinal => true, + _ => false, + } + } + + pub fn any(&self) -> bool { + match *self { + ProjectionMode::Any => true, + _ => false, + } + } +} + + pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 83e02d16c3b81..3ef163f225c4a 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -25,6 +25,7 @@ use super::report_overflow_error; use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation}; use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch}; use super::{ObjectCastObligation, Obligation}; +use super::ProjectionMode; use super::TraitNotObjectSafe; use super::Selection; use super::SelectionResult; @@ -77,98 +78,6 @@ pub struct SelectionContext<'cx, 'tcx:'cx> { /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. intercrate: bool, - - /// Sadly, the behavior of projection varies a bit depending on the - /// stage of compilation. The specifics are given in the - /// documentation for `ProjectionMode`. - projection_mode: ProjectionMode, -} - -/// Depending on the stage of compilation, we want projection to be -/// more or less conservative. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ProjectionMode { - /// At coherence-checking time, we're still constructing the - /// specialization graph, and thus we only project project - /// non-`default` associated types that are defined directly in - /// the applicable impl. (This behavior should be improved over - /// time, to allow for successful projections modulo cycles - /// between different impls). - // TODO: Add tracking issue to do better here. - /// - /// Here's an example that will fail due to the restriction: - /// - /// ``` - /// trait Assoc { - /// type Output; - /// } - /// - /// impl Assoc for T { - /// type Output = bool; - /// } - /// - /// impl Assoc for u8 {} // <- inherits the non-default type from above - /// - /// trait Foo {} - /// impl Foo for u32 {} - /// impl Foo for ::Output {} // <- this projection will fail - /// ``` - /// - /// The projection would succeed if `Output` had been defined - /// directly in the impl for `u8`. - // TODO: Add test - Topmost, - - /// At type-checking time, we refuse to project any associated - /// type that is marked `default`. Non-`default` ("final") types - /// are always projected. This is necessary in general for - /// soundness of specialization. However, we *could* allow - /// projections in fully-monomorphic cases. We choose not to, - /// because we prefer for `default type` to force the type - /// definition to be treated abstractly by any consumers of the - /// impl. Concretely, that means that the following example will - /// fail to compile: - /// - /// ``` - /// trait Assoc { - /// type Output; - /// } - /// - /// impl Assoc for T { - /// default type Output = bool; - /// } - /// - /// fn main() { - /// let <() as Assoc>::Output = true; - /// } - // TODO: Add test - AnyFinal, - - /// At trans time, all projections will succeed. - Any, -} - -impl ProjectionMode { - pub fn topmost(&self) -> bool { - match *self { - ProjectionMode::Topmost => true, - _ => false, - } - } - - pub fn any_final(&self) -> bool { - match *self { - ProjectionMode::AnyFinal => true, - _ => false, - } - } - - pub fn any(&self) -> bool { - match *self { - ProjectionMode::Any => true, - _ => false, - } - } } // A stack that walks back up the stack frame. @@ -348,45 +257,20 @@ pub struct EvaluationCache<'tcx> { hashmap: RefCell, EvaluationResult>> } -pub struct SelectionContextBuilder<'cx, 'tcx: 'cx>(SelectionContext<'cx, 'tcx>); - -impl<'cx, 'tcx> SelectionContextBuilder<'cx, 'tcx> { - pub fn intercrate(mut self) -> Self { - self.0.intercrate = true; - self - } - - pub fn project_any(mut self) -> Self { - self.0.projection_mode = ProjectionMode::Any; - self - } - - pub fn project_any_final(mut self) -> Self { - self.0.projection_mode = ProjectionMode::AnyFinal; - self - } - - pub fn project_topmost(mut self) -> Self { - self.0.projection_mode = ProjectionMode::Topmost; - self - } - - pub fn build(self) -> SelectionContext<'cx, 'tcx> { - self.0 - } -} - -pub fn build_selcx<'cx, 'tcx>(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContextBuilder<'cx, 'tcx> { - SelectionContextBuilder(SelectionContext::new(infcx)) -} - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx: infcx, freshener: infcx.freshener(), intercrate: false, - projection_mode: ProjectionMode::AnyFinal, + } + } + + pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx: infcx, + freshener: infcx.freshener(), + intercrate: true, } } @@ -407,7 +291,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } pub fn projection_mode(&self) -> ProjectionMode { - self.projection_mode + self.infcx.projection_mode() } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index 6fd61741268b6..9cdcc870404d7 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -17,7 +17,7 @@ // See traits/README.md for a bit more detail on how specialization // fits together with the rest of the trait machinery. -use super::{build_selcx, SelectionContext, FulfillmentContext}; +use super::{SelectionContext, FulfillmentContext}; use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig}; use middle::cstore::CrateStore; @@ -25,7 +25,7 @@ use middle::def_id::DefId; use middle::infer::{self, InferCtxt, TypeOrigin}; use middle::region; use middle::subst::{Subst, Substs}; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use middle::ty; use syntax::codemap::DUMMY_SP; @@ -159,7 +159,7 @@ pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> return false } - let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Topmost); // Skiolemize impl1: we want to prove that "for all types matched by impl1, // those types are also matched by impl2". @@ -199,7 +199,7 @@ fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, -> Result, ()> { infcx.probe(|_| { - let selcx = &mut build_selcx(&infcx).project_topmost().build(); + let selcx = &mut SelectionContext::new(&infcx); let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl); let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx, target_impl, diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs index c73219fbc0882..def50766f4e13 100644 --- a/src/librustc/middle/traits/specialize/specialization_graph.rs +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -16,7 +16,7 @@ use super::{Overlap, specializes}; use middle::cstore::CrateStore; use middle::def_id::DefId; use middle::infer; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use middle::ty::{self, ImplOrTraitItem, TraitDef, TypeFoldable}; use syntax::ast::Name; use util::nodemap::DefIdMap; @@ -93,7 +93,7 @@ impl Graph { for slot in possible_siblings.iter_mut() { let possible_sibling = *slot; - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost); let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); if let Some(trait_ref) = overlap { diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index db28963d2c0c6..5af40a3675ff7 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -17,7 +17,7 @@ use middle::def_id::DefId; use middle::subst; use middle::infer; use middle::pat_util; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; use middle::ty::{Disr, ParameterEnvironment}; use middle::ty::TypeVariants::*; @@ -130,7 +130,10 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> { let tcx = self.tcx; // FIXME: (@jroesch) float this code up - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone())); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(self.clone()), + ProjectionMode::AnyFinal); let adt = match self_type.sty { ty::TyStruct(struct_def, substs) => { @@ -542,7 +545,10 @@ impl<'tcx> ty::TyS<'tcx> { -> bool { let tcx = param_env.tcx; - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env.clone())); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(param_env.clone()), + ProjectionMode::AnyFinal); let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, self, bound, span); diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 4a1c115e65599..9186765e6d02c 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -27,6 +27,7 @@ use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::middle::region; use rustc::middle::ty::{self, TyCtxt}; +use rustc::middle::traits::ProjectionMode; use syntax::ast; use syntax::codemap::Span; use rustc_front::hir; @@ -202,7 +203,10 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, debug!("check_loans(body id={})", body.id); let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); - let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(bccx.tcx, + &bccx.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); let mut clcx = CheckLoanCtxt { bccx: bccx, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index e2543b289103a..2d255c054548f 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -24,6 +24,7 @@ use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::middle::region; use rustc::middle::ty::{self, TyCtxt}; +use rustc::middle::traits::ProjectionMode; use syntax::ast; use syntax::codemap::Span; @@ -55,7 +56,10 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, }; let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); - let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(bccx.tcx, + &bccx.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); { let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx); euv.walk_fn(decl, body); @@ -525,7 +529,10 @@ struct StaticInitializerCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> { fn visit_expr(&mut self, ex: &Expr) { if let hir::ExprAddrOf(mutbl, ref base) = ex.node { - let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None); + let infcx = infer::new_infer_ctxt(self.bccx.tcx, + &self.bccx.tcx.tables, + None, + ProjectionMode::AnyFinal); let mc = mc::MemCategorizationContext::new(&infcx); let base_cmt = mc.cat_expr(&base).unwrap(); let borrow_kind = ty::BorrowKind::from_mutbl(mutbl); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 0c906f8eb546c..88027931022e7 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -35,6 +35,7 @@ use middle::def_id::DefId; use middle::subst::Substs; use middle::ty::{self, Ty, TyCtxt}; use middle::ty::adjustment; +use middle::traits::ProjectionMode; use rustc::front::map as hir_map; use util::nodemap::{NodeSet}; use lint::{Level, LateContext, LintContext, LintArray, Lint}; @@ -868,7 +869,10 @@ impl LateLintPass for UnconditionalRecursion { let node_id = tcx.map.as_local_node_id(method.def_id).unwrap(); let param_env = ty::ParameterEnvironment::for_item(tcx, node_id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); let mut selcx = traits::SelectionContext::new(&infcx); match selcx.select(&obligation) { // The method comes from a `T: Trait` bound. diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 2e13e7b42bd6c..4aa8b82ec544e 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -27,7 +27,7 @@ use hair::cx::Cx; use rustc::mir::mir_map::MirMap; use rustc::middle::infer; use rustc::middle::region::CodeExtentData; -use rustc::middle::ty::{self, Ty, TyCtxt}; +use rustc::middle::traits::ProjectionMode; use rustc::util::common::ErrorReported; use rustc::util::nodemap::NodeMap; use rustc_front::hir; @@ -137,7 +137,11 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { }; let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); + match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()), Err(ErrorReported) => {} diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index a9e03bba7db8e..6be7f6c200247 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -35,8 +35,8 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::infer; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; -use rustc::middle::traits; use rustc::middle::ty::{self, Ty, TyCtxt}; +use rustc::middle::traits::{self, ProjectionMode}; use rustc::util::nodemap::NodeMap; use rustc::middle::const_qualif::ConstQualif; use rustc::lint::builtin::CONST_ERR; @@ -92,7 +92,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { None => self.tcx.empty_parameter_environment() }; - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); f(&mut euv::ExprUseVisitor::new(self, &infcx)) } @@ -247,7 +250,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { fn check_static_type(&self, e: &hir::Expr) { let ty = self.tcx.node_id_to_type(e.id); - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + None, + ProjectionMode::AnyFinal); let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic); let mut fulfillment_cx = traits::FulfillmentContext::new(); fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause); diff --git a/src/librustc_passes/rvalues.rs b/src/librustc_passes/rvalues.rs index 7eef69ca50fd0..88048b514e1f5 100644 --- a/src/librustc_passes/rvalues.rs +++ b/src/librustc_passes/rvalues.rs @@ -16,6 +16,7 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::infer; use rustc::middle::mem_categorization as mc; use rustc::middle::ty::{self, TyCtxt, ParameterEnvironment}; +use rustc::middle::traits::ProjectionMode; use rustc_front::hir; use rustc_front::intravisit; @@ -43,7 +44,8 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for RvalueContext<'a, 'tcx> { let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, - Some(param_env.clone())); + Some(param_env.clone()), + ProjectionMode::AnyFinal); let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: ¶m_env }; let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx); euv.walk_fn(fd, b); diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index d1567cc6fa543..f5fbec0b1879e 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -217,6 +217,7 @@ use trans::tvec; use trans::type_of; use trans::Disr; use middle::ty::{self, Ty, TyCtxt}; +use middle::traits::ProjectionMode; use session::config::NoDebugInfo; use util::common::indenter; use util::nodemap::FnvHashMap; @@ -1475,7 +1476,9 @@ fn is_discr_reassigned(bcx: Block, discr: &hir::Expr, body: &hir::Expr) -> bool reassigned: false }; { - let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), &bcx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), + &bcx.tcx().tables, + ProjectionMode::Any); let mut visitor = euv::ExprUseVisitor::new(&mut rc, &infcx); visitor.walk_expr(body); } diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs index 009d43e813ebc..d93d32f8e0d06 100644 --- a/src/librustc_trans/trans/attributes.rs +++ b/src/librustc_trans/trans/attributes.rs @@ -13,6 +13,7 @@ use libc::{c_uint, c_ulonglong}; use llvm::{self, ValueRef, AttrHelper}; use middle::ty; use middle::infer; +use middle::traits::ProjectionMode; use session::config::NoDebugInfo; use syntax::abi::Abi; pub use syntax::attr::InlineAttr; @@ -133,7 +134,9 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx let (fn_sig, abi, env_ty) = match fn_type.sty { ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => (&f.sig, f.abi, None), ty::TyClosure(closure_did, ref substs) => { - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), + &ccx.tcx().tables, + ProjectionMode::Any); function_type = infcx.closure_type(closure_did, substs); let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); (&function_type.sig, Abi::RustCall, Some(self_type)) diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index 95ca250e84445..11c03fe7a7dc7 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -13,6 +13,7 @@ use back::link::{self, mangle_internal_name_by_path_and_seq}; use llvm::{ValueRef, get_params}; use middle::def_id::DefId; use middle::infer; +use middle::traits::ProjectionMode; use trans::adt; use trans::attributes; use trans::base::*; @@ -206,7 +207,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, // this function (`trans_closure`) is invoked at the point // of the closure expression. - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any); let function_type = infcx.closure_type(closure_def_id, closure_substs); let freevars: Vec = @@ -329,7 +330,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx.tn().val_to_string(llreffn)); let tcx = ccx.tcx(); - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any); // Find a version of the closure type. Substitute static for the // region since it doesn't really matter. diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index dd144de6ec5e4..0aa69dec253a1 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -37,8 +37,8 @@ use trans::machine; use trans::monomorphize; use trans::type_::Type; use trans::type_of; -use middle::traits; use middle::ty::{self, Ty, TyCtxt}; +use middle::traits::{self, SelectionContext, ProjectionMode}; use middle::ty::fold::{TypeFolder, TypeFoldable}; use rustc_front::hir; use rustc::mir::repr::Mir; @@ -1137,8 +1137,8 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut selcx = traits::build_selcx(&infcx).project_any().build(); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + let mut selcx = SelectionContext::new(&infcx); let obligation = traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID), @@ -1198,8 +1198,8 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, predicates); let tcx = ccx.tcx(); - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut selcx = traits::build_selcx(&infcx).project_any().build(); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + let mut selcx = SelectionContext::new(&infcx); let mut fulfill_cx = traits::FulfillmentContext::new(); let cause = traits::ObligationCause::dummy(); let traits::Normalized { value: predicates, obligations } = diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 38e456c068829..0c512200ff3d5 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -22,6 +22,7 @@ use llvm::{self, ValueRef}; use middle::ty; use middle::infer; +use middle::traits::ProjectionMode; use syntax::abi::Abi; use trans::attributes; use trans::base; @@ -111,7 +112,9 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, (&f.sig, f.abi, None) } ty::TyClosure(closure_did, ref substs) => { - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), + &ccx.tcx().tables, + ProjectionMode::Any); function_type = infcx.closure_type(closure_did, substs); let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); let llenvironment_type = type_of::type_of_explicit_arg(ccx, self_type); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 28e9fcd47ff02..0f5c1cf87f1e1 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -17,7 +17,7 @@ use middle::def_id::DefId; use middle::infer; use middle::subst::{Subst, Substs}; use middle::subst; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use trans::base::*; use trans::build::*; use trans::callee::{Callee, Virtual, ArgVals, @@ -488,7 +488,7 @@ pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { Some(node_item) => { diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index ff7b809577f64..899f79b3dff94 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -10,8 +10,8 @@ use middle::free_region::FreeRegionMap; use middle::infer::{self, TypeOrigin}; -use middle::traits; use middle::ty::{self, TyCtxt}; +use middle::traits::{self, ProjectionMode}; use middle::subst::{self, Subst, Substs, VecPerParamSpace}; use syntax::ast; @@ -42,7 +42,7 @@ pub fn compare_impl_method<'tcx>(tcx: &TyCtxt<'tcx>, debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}", impl_trait_ref); - let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); let mut fulfillment_cx = traits::FulfillmentContext::new(); let trait_to_impl_substs = &impl_trait_ref.substs; @@ -416,7 +416,7 @@ pub fn compare_const_impl<'tcx>(tcx: &TyCtxt<'tcx>, debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); let mut fulfillment_cx = traits::FulfillmentContext::new(); // The below is for the most part highly similar to the procedure diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 4ebe4c25dd1d3..4ed1bab46b2d9 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -15,8 +15,8 @@ use middle::free_region::FreeRegionMap; use middle::infer; use middle::region; use middle::subst::{self, Subst}; -use middle::traits; use middle::ty::{self, Ty, TyCtxt}; +use middle::traits::{self, ProjectionMode}; use util::nodemap::FnvHashSet; use syntax::ast; @@ -82,7 +82,10 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( // check that the impl type can be made to match the trait type. let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(impl_param_env), + ProjectionMode::AnyFinal); let mut fulfillment_cx = traits::FulfillmentContext::new(); let named_type = tcx.lookup_item_type(self_type_did).ty; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index bb94d80474afb..cdff78d01e3e2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -92,7 +92,7 @@ use middle::infer; use middle::infer::{TypeOrigin, TypeTrace, type_variable}; use middle::pat_util::{self, pat_id_map}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; -use middle::traits::{self, report_fulfillment_errors}; +use middle::traits::{self, report_fulfillment_errors, ProjectionMode}; use middle::ty::{GenericPredicates, TypeScheme}; use middle::ty::{ParamTy, ParameterEnvironment}; use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; @@ -307,7 +307,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { -> Inherited<'a, 'tcx> { Inherited { - infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env)), + infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), ProjectionMode::AnyFinal), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), locals: RefCell::new(NodeMap()), tables: tables, diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index d62e7dd43f081..278d4d8b5b44a 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -18,8 +18,8 @@ use middle::def_id::DefId; use middle::lang_items::UnsizeTraitLangItem; use middle::subst::{self, Subst}; -use middle::traits; use middle::ty::{self, TyCtxt, TypeFoldable}; +use middle::traits::{self, ProjectionMode}; use middle::ty::{ImplOrTraitItemId, ConstTraitItemId}; use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment}; use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError}; @@ -384,7 +384,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)", source, target); - let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env), ProjectionMode::Topmost); let origin = TypeOrigin::Misc(span); let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>, @@ -529,7 +529,10 @@ pub fn report_duplicate_item<'tcx>(tcx: &TyCtxt<'tcx>, sp: Span, name: ast::Name pub fn check_coherence(crate_context: &CrateCtxt) { let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence); - let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None); + let infcx = new_infer_ctxt(crate_context.tcx, + &crate_context.tcx.tables, + None, + ProjectionMode::Topmost); CoherenceChecker { crate_context: crate_context, inference_context: infcx, diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 125c3d426a803..936be80919406 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -106,6 +106,7 @@ use middle::def::Def; use middle::infer::{self, TypeOrigin}; use middle::subst::Substs; use middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use middle::traits::ProjectionMode; use session::{config, CompileResult}; use util::common::time; use rustc_front::hir; @@ -196,7 +197,7 @@ fn require_same_types<'a, 'tcx, M>(tcx: &TyCtxt<'tcx>, { let result = match maybe_infcx { None => { - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); infer::mk_eqty(&infcx, t1_is_expected, TypeOrigin::Misc(span), t1, t2) } Some(infcx) => { From 8f0e73ef55f6cf837555a0c144c00844eefa97b9 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 8 Mar 2016 15:23:52 -0800 Subject: [PATCH 29/37] Address review comments --- src/librustc/middle/traits/project.rs | 10 +- src/librustc/middle/traits/specialize/mod.rs | 140 +++++++----------- .../traits/specialize/specialization_graph.rs | 21 +-- src/librustc_typeck/check/mod.rs | 4 +- .../specialization-default-projection.rs | 16 +- .../specialization-default-types.rs | 4 +- .../specialization-feature-gate-default.rs | 4 +- .../specialization-feature-gate-overlap.rs | 4 +- .../compile-fail/specialization-no-default.rs | 3 + 9 files changed, 99 insertions(+), 107 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 58d36b45ecb0a..e1d811d450d5a 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -100,21 +100,21 @@ pub enum ProjectionMode { } impl ProjectionMode { - pub fn topmost(&self) -> bool { + pub fn is_topmost(&self) -> bool { match *self { ProjectionMode::Topmost => true, _ => false, } } - pub fn any_final(&self) -> bool { + pub fn is_any_final(&self) -> bool { match *self { ProjectionMode::AnyFinal => true, _ => false, } } - pub fn any(&self) -> bool { + pub fn is_any(&self) -> bool { match *self { ProjectionMode::Any => true, _ => false, @@ -665,7 +665,7 @@ fn project_type<'cx,'tcx>( // In Any (i.e. trans) mode, all projections succeed; // otherwise, we need to be sensitive to `default` and // specialization. - if !selcx.projection_mode().any() { + if !selcx.projection_mode().is_any() { if let ProjectionTyCandidate::Impl(ref impl_data) = candidate { if let Some(node_item) = assoc_ty_def(selcx, impl_data.impl_def_id, @@ -1116,7 +1116,7 @@ fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>, impl_def_id: Def { let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id; - if selcx.projection_mode().topmost() { + if selcx.projection_mode().is_topmost() { let impl_node = specialization_graph::Node::Impl(impl_def_id); for item in impl_node.items(selcx.tcx()) { if let ty::TypeTraitItem(assoc_ty) = item { diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index 9cdcc870404d7..b557db9a8b2ac 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -25,7 +25,7 @@ use middle::def_id::DefId; use middle::infer::{self, InferCtxt, TypeOrigin}; use middle::region; use middle::subst::{Subst, Substs}; -use middle::traits::{self, ProjectionMode}; +use middle::traits::ProjectionMode; use middle::ty; use syntax::codemap::DUMMY_SP; @@ -41,45 +41,43 @@ pub struct Overlap<'a, 'tcx: 'a> { /// Given a subst for the requested impl, translate it to a subst /// appropriate for the actual item definition (whether it be in that impl, /// a parent impl, or the trait). -// -// When we have selected one impl, but are actually using item definitions from -// a parent impl providing a default, we need a way to translate between the -// type parameters of the two impls. Here the `source_impl` is the one we've -// selected, and `source_substs` is a substitution of its generics (and -// possibly some relevant `FnSpace` variables as well). And `target_node` is -// the impl/trait we're actually going to get the definition from. The resulting -// substitution will map from `target_node`'s generics to `source_impl`'s -// generics as instantiated by `source_subst`. -// -// For example, consider the following scenario: -// -// ```rust -// trait Foo { ... } -// impl Foo for (T, U) { ... } // target impl -// impl Foo for (V, V) { ... } // source impl -// ``` -// -// Suppose we have selected "source impl" with `V` instantiated with `u32`. -// This function will produce a substitution with `T` and `U` both mapping to `u32`. -// -// Where clauses add some trickiness here, because they can be used to "define" -// an argument indirectly: -// -// ```rust -// impl<'a, I, T: 'a> Iterator for Cloned -// where I: Iterator, T: Clone -// ``` -// -// In a case like this, the substitution for `T` is determined indirectly, -// through associated type projection. We deal with such cases by using -// *fulfillment* to relate the two impls, requiring that all projections are -// resolved. +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics (and +/// possibly some relevant `FnSpace` variables as well). And `target_node` is +/// the impl/trait we're actually going to get the definition from. The resulting +/// substitution will map from `target_node`'s generics to `source_impl`'s +/// generics as instantiated by `source_subst`. +/// +/// For example, consider the following scenario: +/// +/// ```rust +/// trait Foo { ... } +/// impl Foo for (T, U) { ... } // target impl +/// impl Foo for (V, V) { ... } // source impl +/// ``` +/// +/// Suppose we have selected "source impl" with `V` instantiated with `u32`. +/// This function will produce a substitution with `T` and `U` both mapping to `u32`. +/// +/// Where clauses add some trickiness here, because they can be used to "define" +/// an argument indirectly: +/// +/// ```rust +/// impl<'a, I, T: 'a> Iterator for Cloned +/// where I: Iterator, T: Clone +/// ``` +/// +/// In a case like this, the substitution for `T` is determined indirectly, +/// through associated type projection. We deal with such cases by using +/// *fulfillment* to relate the two impls, requiring that all projections are +/// resolved. pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, source_impl: DefId, source_substs: Substs<'tcx>, target_node: specialization_graph::Node) - -> Substs<'tcx> -{ + -> Substs<'tcx> { let source_trait_ref = infcx.tcx .impl_trait_ref(source_impl) .unwrap() @@ -116,29 +114,16 @@ pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, target_substs.with_method_from_subst(&source_substs) } - -fn skolemizing_subst_for_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, - impl_def_id: DefId) - -> Substs<'tcx> -{ - let impl_generics = infcx.tcx.lookup_item_type(impl_def_id).generics; - - let types = impl_generics.types.map(|def| infcx.tcx.mk_param_from_def(def)); - - // TODO: figure out what we actually want here - let regions = impl_generics.regions.map(|_| ty::Region::ReStatic); - // |d| infcx.next_region_var(infer::RegionVariableOrigin::EarlyBoundRegion(span, d.name))); - - Substs::new(types, regions) -} - /// Is impl1 a specialization of impl2? /// /// Specialization is determined by the sets of types to which the impls apply; /// impl1 specializes impl2 if it applies to a subset of the types impl2 applies /// to. pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { - if !tcx.sess.features.borrow().specialization { + // The feature gate should prevent introducing new specializations, but not + // taking advantage of upstream ones. + if !tcx.sess.features.borrow().specialization && + (impl1_def_id.is_local() || impl2_def_id.is_local()) { return false; } @@ -156,33 +141,24 @@ pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> // Currently we do not allow e.g. a negative impl to specialize a positive one if tcx.trait_impl_polarity(impl1_def_id) != tcx.trait_impl_polarity(impl2_def_id) { - return false + return false; } let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Topmost); - // Skiolemize impl1: we want to prove that "for all types matched by impl1, - // those types are also matched by impl2". - let impl1_substs = skolemizing_subst_for_impl(&infcx, impl1_def_id); - let (impl1_trait_ref, impl1_obligations) = { - let selcx = &mut SelectionContext::new(&infcx); - impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) - }; + // create a parameter environment corresponding to a (skolemized) instantiation of impl1 + let scheme = tcx.lookup_item_type(impl1_def_id); + let predicates = tcx.lookup_predicates(impl1_def_id); + let penv = tcx.construct_parameter_environment(DUMMY_SP, + &scheme.generics, + &predicates, + region::DUMMY_CODE_EXTENT); + let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id) + .unwrap() + .subst(tcx, &penv.free_substs); - // Add impl1's obligations as assumptions to the environment. - let impl1_predicates: Vec<_> = impl1_obligations.iter() - .cloned() - .map(|oblig| oblig.predicate) - .collect(); - infcx.parameter_environment = ty::ParameterEnvironment { - tcx: tcx, - free_substs: impl1_substs, - implicit_region_bound: ty::ReEmpty, // TODO: is this OK? - caller_bounds: impl1_predicates, - selection_cache: traits::SelectionCache::new(), - evaluation_cache: traits::EvaluationCache::new(), - free_id_outlive: region::DUMMY_CODE_EXTENT, // TODO: is this OK? - }; + // Install the parameter environment, which means we take the predicates of impl1 as assumptions: + infcx.parameter_environment = penv; // Attempt to prove that impl2 applies, given all of the above. fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok() @@ -196,9 +172,8 @@ pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, source_trait_ref: ty::TraitRef<'tcx>, target_impl: DefId) - -> Result, ()> -{ - infcx.probe(|_| { + -> Result, ()> { + infcx.commit_if_ok(|_| { let selcx = &mut SelectionContext::new(&infcx); let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl); let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx, @@ -227,23 +202,20 @@ fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { // no dice! - debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} \ - given {:?}", + debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ + {:?}", source_trait_ref, target_trait_ref, errors, infcx.parameter_environment.caller_bounds); Err(()) } else { - debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses \ - elided)", + debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", source_trait_ref, target_trait_ref); // Now resolve the *substitution* we built for the target earlier, replacing // the inference variables inside with whatever we got from fulfillment. - - // TODO: should this use `fully_resolve` instead? Ok(infcx.resolve_type_vars_if_possible(&target_substs)) } }) diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs index def50766f4e13..77588a3e0708b 100644 --- a/src/librustc/middle/traits/specialize/specialization_graph.rs +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -65,14 +65,15 @@ impl Graph { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; - debug!("inserting TraitRef {:?} into specialization graph", 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 supress // bogus errors). if trait_ref.references_error() { - debug!("Inserting dummy node for erroneous TraitRef {:?}, \ + debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \ impl_def_id={:?}, trait_def_id={:?}", trait_ref, impl_def_id, trait_def_id); @@ -101,15 +102,15 @@ impl Graph { let ge = specializes(tcx, possible_sibling, impl_def_id); if le && !ge { - let parent_trait_ref = tcx.impl_trait_ref(possible_sibling).unwrap(); - debug!("descending as child of TraitRef {:?}", parent_trait_ref); + debug!("descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap()); // the impl specializes possible_sibling parent = possible_sibling; continue 'descend; } else if ge && !le { - let child_trait_ref = tcx.impl_trait_ref(possible_sibling).unwrap(); - debug!("placing as parent of TraitRef {:?}", child_trait_ref); + debug!("placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap()); // possible_sibling specializes the impl *slot = impl_def_id; @@ -158,10 +159,10 @@ impl Graph { } } -#[derive(Debug, Copy, Clone)] /// A node in the specialization graph is either an impl or a trait /// definition; either can serve as a source of item definitions. /// There is always exactly one trait definition node: the root. +#[derive(Debug, Copy, Clone)] pub enum Node { Impl(DefId), Trait(DefId), @@ -316,7 +317,7 @@ impl<'a, 'tcx> Iterator for ConstDefs<'a, 'tcx> { } impl<'a, 'tcx> Ancestors<'a, 'tcx> { - /// Seach the items from the given ancestors, returning each type definition + /// Search the items from the given ancestors, returning each type definition /// with the given name. pub fn type_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> { let iter = self.flat_map(move |node| { @@ -337,7 +338,7 @@ impl<'a, 'tcx> Ancestors<'a, 'tcx> { TypeDefs { iter: Box::new(iter) } } - /// Seach the items from the given ancestors, returning each fn definition + /// Search the items from the given ancestors, returning each fn definition /// with the given name. pub fn fn_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> { let iter = self.flat_map(move |node| { @@ -358,7 +359,7 @@ impl<'a, 'tcx> Ancestors<'a, 'tcx> { FnDefs { iter: Box::new(iter) } } - /// Seach the items from the given ancestors, returning each const + /// Search the items from the given ancestors, returning each const /// definition with the given name. pub fn const_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> { let iter = self.flat_map(move |node| { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index cdff78d01e3e2..534f10103b535 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -870,8 +870,8 @@ fn report_forbidden_specialization(tcx: &ty::ctxt, { let mut err = struct_span_err!( tcx.sess, impl_item.span, E0520, - "item `{}` is provided by an implementation that specializes \ - another, but the item in the parent implementations is not \ + "item `{}` is provided by an `impl` that specializes \ + another, but the item in the parent `impl` is not \ marked `default` and so it cannot be specialized.", impl_item.name); diff --git a/src/test/compile-fail/specialization-default-projection.rs b/src/test/compile-fail/specialization-default-projection.rs index 3f85a503f62db..377838f2a080c 100644 --- a/src/test/compile-fail/specialization-default-projection.rs +++ b/src/test/compile-fail/specialization-default-projection.rs @@ -25,10 +25,22 @@ impl Foo for u8 { } fn generic() -> ::Assoc { - () //~ ERROR + // `T` could be some downstream crate type that specializes (or, + // for that matter, `u8`). + + () //~ ERROR E0308 +} + +fn monomorphic() -> () { + // Even though we know that `()` is not specialized in a + // downstream crate, typeck refuses to project here. + + generic::<()>() //~ ERROR E0308 } fn main() { + // No error here, we CAN project from `u8`, as there is no `default` + // in that impl. let s: String = generic::(); - println!("{}", s); // bad news + println!("{}", s); // bad news if this all compiles } diff --git a/src/test/compile-fail/specialization-default-types.rs b/src/test/compile-fail/specialization-default-types.rs index dce1db06a92b3..3c2e3d5a36c37 100644 --- a/src/test/compile-fail/specialization-default-types.rs +++ b/src/test/compile-fail/specialization-default-types.rs @@ -22,7 +22,7 @@ trait Example { impl Example for T { default type Output = Box; default fn generate(self) -> Self::Output { - Box::new(self) //~ ERROR + Box::new(self) //~ ERROR E0308 } } @@ -32,7 +32,7 @@ impl Example for bool { } fn trouble(t: T) -> Box { - Example::generate(t) //~ ERROR + Example::generate(t) //~ ERROR E0308 } fn weaponize() -> bool { diff --git a/src/test/compile-fail/specialization-feature-gate-default.rs b/src/test/compile-fail/specialization-feature-gate-default.rs index caac21278d464..e7c194ce84df9 100644 --- a/src/test/compile-fail/specialization-feature-gate-default.rs +++ b/src/test/compile-fail/specialization-feature-gate-default.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Check that specialization must be ungated to use the `default` keyword + trait Foo { fn foo(&self); } impl Foo for T { - default fn foo(&self) {} //~ ERROR + default fn foo(&self) {} //~ ERROR specialization is unstable } fn main() {} diff --git a/src/test/compile-fail/specialization-feature-gate-overlap.rs b/src/test/compile-fail/specialization-feature-gate-overlap.rs index a7918e4426d9a..d11ab56ff7e87 100644 --- a/src/test/compile-fail/specialization-feature-gate-overlap.rs +++ b/src/test/compile-fail/specialization-feature-gate-overlap.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Check that writing an overlapping impl is not allow unless specialization is ungated. + trait Foo { fn foo(&self); } @@ -16,7 +18,7 @@ impl Foo for T { fn foo(&self) {} } -impl Foo for u8 { //~ ERROR +impl Foo for u8 { //~ ERROR E0119 fn foo(&self) {} } diff --git a/src/test/compile-fail/specialization-no-default.rs b/src/test/compile-fail/specialization-no-default.rs index 88cd00e69c422..961561685437a 100644 --- a/src/test/compile-fail/specialization-no-default.rs +++ b/src/test/compile-fail/specialization-no-default.rs @@ -10,6 +10,9 @@ #![feature(specialization)] +// Check a number of scenarios in which one impl tries to override another, +// without correctly using `default`. + //////////////////////////////////////////////////////////////////////////////// // Test 1: one layer of specialization, multiple methods, missing `default` //////////////////////////////////////////////////////////////////////////////// From 35437c7cf6182b64b53d079367d8ca9822db9a33 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 8 Mar 2016 16:09:39 -0800 Subject: [PATCH 30/37] Fixes after a rebase --- src/librustc/middle/traits/project.rs | 3 +-- src/librustc/middle/traits/specialize/mod.rs | 4 ++-- .../traits/specialize/specialization_graph.rs | 14 +++++++------- src/librustc/middle/ty/trait_def.rs | 2 +- src/librustc_metadata/decoder.rs | 1 - src/librustc_metadata/encoder.rs | 1 + src/librustc_mir/mir_map.rs | 1 + src/librustc_trans/trans/meth.rs | 2 +- src/librustc_typeck/check/mod.rs | 4 ++-- src/librustc_typeck/coherence/overlap.rs | 1 - src/libsyntax/feature_gate.rs | 5 ++--- 11 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index e1d811d450d5a..0c857692ad11c 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -26,9 +26,8 @@ use super::util; use middle::def_id::DefId; use middle::infer::{self, TypeOrigin}; use middle::subst::Subst; -use middle::ty::{self, ToPredicate, RegionEscape, HasTypeFlags, ToPolyTraitRef, Ty, TyCtxt}; +use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt}; use middle::ty::fold::{TypeFoldable, TypeFolder}; -use rustc_front::hir; use syntax::parse::token; use syntax::ast; use util::common::FN_OUTPUT_NAME; diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index b557db9a8b2ac..a5b3c667fc06d 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -26,7 +26,7 @@ use middle::infer::{self, InferCtxt, TypeOrigin}; use middle::region; use middle::subst::{Subst, Substs}; use middle::traits::ProjectionMode; -use middle::ty; +use middle::ty::{self, TyCtxt}; use syntax::codemap::DUMMY_SP; pub mod specialization_graph; @@ -119,7 +119,7 @@ pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, /// Specialization is determined by the sets of types to which the impls apply; /// impl1 specializes impl2 if it applies to a subset of the types impl2 applies /// to. -pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { +pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { // The feature gate should prevent introducing new specializations, but not // taking advantage of upstream ones. if !tcx.sess.features.borrow().specialization && diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs index 77588a3e0708b..4af8e1fe0a831 100644 --- a/src/librustc/middle/traits/specialize/specialization_graph.rs +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -17,7 +17,7 @@ use middle::cstore::CrateStore; use middle::def_id::DefId; use middle::infer; use middle::traits::{self, ProjectionMode}; -use middle::ty::{self, ImplOrTraitItem, TraitDef, TypeFoldable}; +use middle::ty::{self, TyCtxt, ImplOrTraitItem, TraitDef, TypeFoldable}; use syntax::ast::Name; use util::nodemap::DefIdMap; @@ -57,7 +57,7 @@ impl Graph { /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. pub fn insert<'a, 'tcx>(&mut self, - tcx: &'a ty::ctxt<'tcx>, + tcx: &'a TyCtxt<'tcx>, impl_def_id: DefId) -> Result<(), Overlap<'a, 'tcx>> { assert!(impl_def_id.is_local()); @@ -177,7 +177,7 @@ impl Node { } /// Iterate over the items defined directly by the given (impl or trait) node. - pub fn items<'a, 'tcx>(&self, tcx: &'a ty::ctxt<'tcx>) -> NodeItems<'a, 'tcx> { + pub fn items<'a, 'tcx>(&self, tcx: &'a TyCtxt<'tcx>) -> NodeItems<'a, 'tcx> { match *self { Node::Impl(impl_def_id) => { NodeItems::Impl { @@ -207,7 +207,7 @@ impl Node { /// An iterator over the items defined within a trait or impl. pub enum NodeItems<'a, 'tcx: 'a> { Impl { - tcx: &'a ty::ctxt<'tcx>, + tcx: &'a TyCtxt<'tcx>, items: cell::Ref<'a, Vec>, idx: usize, }, @@ -319,7 +319,7 @@ impl<'a, 'tcx> Iterator for ConstDefs<'a, 'tcx> { impl<'a, 'tcx> Ancestors<'a, 'tcx> { /// Search the items from the given ancestors, returning each type definition /// with the given name. - pub fn type_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> { + pub fn type_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> { let iter = self.flat_map(move |node| { node.items(tcx) .filter_map(move |item| { @@ -340,7 +340,7 @@ impl<'a, 'tcx> Ancestors<'a, 'tcx> { /// Search the items from the given ancestors, returning each fn definition /// with the given name. - pub fn fn_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> { + pub fn fn_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> { let iter = self.flat_map(move |node| { node.items(tcx) .filter_map(move |item| { @@ -361,7 +361,7 @@ impl<'a, 'tcx> Ancestors<'a, 'tcx> { /// Search the items from the given ancestors, returning each const /// definition with the given name. - pub fn const_defs(self, tcx: &'a ty::ctxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> { + pub fn const_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> { let iter = self.flat_map(move |node| { node.items(tcx) .filter_map(move |item| { diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index c4e820bde8f57..3d7b3bf263425 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -192,7 +192,7 @@ impl<'tcx> TraitDef<'tcx> { /// overlap information if the impl overlaps but does not specialize an /// existing impl. pub fn add_impl_for_specialization<'a>(&self, - tcx: &'a ctxt<'tcx>, + tcx: &'a TyCtxt<'tcx>, impl_def_id: DefId) -> Result<(), traits::Overlap<'a, 'tcx>> { assert!(impl_def_id.is_local()); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index cf65d7a522ad1..38a2a7794bcbd 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -34,7 +34,6 @@ use middle::lang_items; use middle::subst; use middle::ty::{ImplContainer, TraitContainer}; use middle::ty::{self, Ty, TyCtxt, TypeFoldable, VariantKind}; -use middle::traits; use rustc_const_eval::ConstInt; diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 707657cfa26bb..41baa0b159148 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -25,6 +25,7 @@ use middle::def_id::{CRATE_DEF_INDEX, DefId}; use middle::dependency_format::Linkage; use middle::stability; use middle::subst; +use middle::traits::specialization_graph; use middle::ty::{self, Ty, TyCtxt}; use middle::ty::util::IntTypeExt; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 4aa8b82ec544e..13521de78af28 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -28,6 +28,7 @@ use rustc::mir::mir_map::MirMap; use rustc::middle::infer; use rustc::middle::region::CodeExtentData; use rustc::middle::traits::ProjectionMode; +use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::util::common::ErrorReported; use rustc::util::nodemap::NodeMap; use rustc_front::hir; diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 0f5c1cf87f1e1..2965e44bc1748 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -478,7 +478,7 @@ pub struct ImplMethod<'tcx> { } /// Locates the applicable definition of a method, given its name. -pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, +pub fn get_impl_method<'tcx>(tcx: &TyCtxt<'tcx>, impl_def_id: DefId, substs: Substs<'tcx>, name: Name) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 534f10103b535..0d5e25efd68c9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -864,7 +864,7 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env); } -fn report_forbidden_specialization(tcx: &ty::ctxt, +fn report_forbidden_specialization(tcx: &TyCtxt, impl_item: &hir::ImplItem, parent_impl: DefId) { @@ -887,7 +887,7 @@ fn report_forbidden_specialization(tcx: &ty::ctxt, err.emit(); } -fn check_specialization_validity<'tcx>(tcx: &ty::ctxt<'tcx>, trait_def: &ty::TraitDef<'tcx>, +fn check_specialization_validity<'tcx>(tcx: &TyCtxt<'tcx>, trait_def: &ty::TraitDef<'tcx>, impl_id: DefId, impl_item: &hir::ImplItem) { let ancestors = trait_def.ancestors(impl_id); diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 4a5dbe20b7879..e4994eaa25528 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -15,7 +15,6 @@ use middle::cstore::CrateStore; use middle::traits; use middle::ty::{self, TyCtxt}; -use middle::infer; use syntax::ast; use rustc::dep_graph::DepNode; use rustc_front::hir; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index a5f0fbbd0940b..fbaf28332c42c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -247,11 +247,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status ("inclusive_range_syntax", "1.7.0", Some(28237), Active), // `expr?` - ("question_mark", "1.9.0", Some(31436), Active) + ("question_mark", "1.9.0", Some(31436), Active), // impl specialization (RFC 1210) - // TODO: update with issue number (once it exists), before landing - ("specialization", "1.7.0", None, Active), + ("specialization", "1.7.0", Some(31844), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) From 326201657a912d9a291da057d82ddd4576ae04f2 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 8 Mar 2016 16:14:13 -0800 Subject: [PATCH 31/37] Move tests to dedicated subdirectories --- .../{ => specialization}/specialization-default-projection.rs | 0 .../{ => specialization}/specialization-default-types.rs | 0 .../{ => specialization}/specialization-feature-gate-default.rs | 0 .../{ => specialization}/specialization-feature-gate-overlap.rs | 0 .../{ => specialization}/specialization-no-default.rs | 0 .../{ => specialization}/specialization-overlap-negative.rs | 0 .../compile-fail/{ => specialization}/specialization-overlap.rs | 0 .../compile-fail/{ => specialization}/specialization-polarity.rs | 0 .../{ => specialization}/specialization-allowed-cross-crate.rs | 0 .../run-pass/{ => specialization}/specialization-assoc-fns.rs | 0 src/test/run-pass/{ => specialization}/specialization-basics.rs | 0 .../{ => specialization}/specialization-cross-crate-defaults.rs | 0 .../run-pass/{ => specialization}/specialization-cross-crate.rs | 0 .../{ => specialization}/specialization-default-methods.rs | 0 .../run-pass/{ => specialization}/specialization-on-projection.rs | 0 .../run-pass/{ => specialization}/specialization-out-of-order.rs | 0 .../{ => specialization}/specialization-projection-alias.rs | 0 .../run-pass/{ => specialization}/specialization-projection.rs | 0 .../run-pass/{ => specialization}/specialization-super-traits.rs | 0 .../specialization-translate-projections-with-params.rs | 0 .../{ => specialization}/specialization-translate-projections.rs | 0 21 files changed, 0 insertions(+), 0 deletions(-) rename src/test/compile-fail/{ => specialization}/specialization-default-projection.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-default-types.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-feature-gate-default.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-feature-gate-overlap.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-no-default.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-overlap-negative.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-overlap.rs (100%) rename src/test/compile-fail/{ => specialization}/specialization-polarity.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-allowed-cross-crate.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-assoc-fns.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-basics.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-cross-crate-defaults.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-cross-crate.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-default-methods.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-on-projection.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-out-of-order.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-projection-alias.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-projection.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-super-traits.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-translate-projections-with-params.rs (100%) rename src/test/run-pass/{ => specialization}/specialization-translate-projections.rs (100%) diff --git a/src/test/compile-fail/specialization-default-projection.rs b/src/test/compile-fail/specialization/specialization-default-projection.rs similarity index 100% rename from src/test/compile-fail/specialization-default-projection.rs rename to src/test/compile-fail/specialization/specialization-default-projection.rs diff --git a/src/test/compile-fail/specialization-default-types.rs b/src/test/compile-fail/specialization/specialization-default-types.rs similarity index 100% rename from src/test/compile-fail/specialization-default-types.rs rename to src/test/compile-fail/specialization/specialization-default-types.rs diff --git a/src/test/compile-fail/specialization-feature-gate-default.rs b/src/test/compile-fail/specialization/specialization-feature-gate-default.rs similarity index 100% rename from src/test/compile-fail/specialization-feature-gate-default.rs rename to src/test/compile-fail/specialization/specialization-feature-gate-default.rs diff --git a/src/test/compile-fail/specialization-feature-gate-overlap.rs b/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs similarity index 100% rename from src/test/compile-fail/specialization-feature-gate-overlap.rs rename to src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs diff --git a/src/test/compile-fail/specialization-no-default.rs b/src/test/compile-fail/specialization/specialization-no-default.rs similarity index 100% rename from src/test/compile-fail/specialization-no-default.rs rename to src/test/compile-fail/specialization/specialization-no-default.rs diff --git a/src/test/compile-fail/specialization-overlap-negative.rs b/src/test/compile-fail/specialization/specialization-overlap-negative.rs similarity index 100% rename from src/test/compile-fail/specialization-overlap-negative.rs rename to src/test/compile-fail/specialization/specialization-overlap-negative.rs diff --git a/src/test/compile-fail/specialization-overlap.rs b/src/test/compile-fail/specialization/specialization-overlap.rs similarity index 100% rename from src/test/compile-fail/specialization-overlap.rs rename to src/test/compile-fail/specialization/specialization-overlap.rs diff --git a/src/test/compile-fail/specialization-polarity.rs b/src/test/compile-fail/specialization/specialization-polarity.rs similarity index 100% rename from src/test/compile-fail/specialization-polarity.rs rename to src/test/compile-fail/specialization/specialization-polarity.rs diff --git a/src/test/run-pass/specialization-allowed-cross-crate.rs b/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs similarity index 100% rename from src/test/run-pass/specialization-allowed-cross-crate.rs rename to src/test/run-pass/specialization/specialization-allowed-cross-crate.rs diff --git a/src/test/run-pass/specialization-assoc-fns.rs b/src/test/run-pass/specialization/specialization-assoc-fns.rs similarity index 100% rename from src/test/run-pass/specialization-assoc-fns.rs rename to src/test/run-pass/specialization/specialization-assoc-fns.rs diff --git a/src/test/run-pass/specialization-basics.rs b/src/test/run-pass/specialization/specialization-basics.rs similarity index 100% rename from src/test/run-pass/specialization-basics.rs rename to src/test/run-pass/specialization/specialization-basics.rs diff --git a/src/test/run-pass/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs similarity index 100% rename from src/test/run-pass/specialization-cross-crate-defaults.rs rename to src/test/run-pass/specialization/specialization-cross-crate-defaults.rs diff --git a/src/test/run-pass/specialization-cross-crate.rs b/src/test/run-pass/specialization/specialization-cross-crate.rs similarity index 100% rename from src/test/run-pass/specialization-cross-crate.rs rename to src/test/run-pass/specialization/specialization-cross-crate.rs diff --git a/src/test/run-pass/specialization-default-methods.rs b/src/test/run-pass/specialization/specialization-default-methods.rs similarity index 100% rename from src/test/run-pass/specialization-default-methods.rs rename to src/test/run-pass/specialization/specialization-default-methods.rs diff --git a/src/test/run-pass/specialization-on-projection.rs b/src/test/run-pass/specialization/specialization-on-projection.rs similarity index 100% rename from src/test/run-pass/specialization-on-projection.rs rename to src/test/run-pass/specialization/specialization-on-projection.rs diff --git a/src/test/run-pass/specialization-out-of-order.rs b/src/test/run-pass/specialization/specialization-out-of-order.rs similarity index 100% rename from src/test/run-pass/specialization-out-of-order.rs rename to src/test/run-pass/specialization/specialization-out-of-order.rs diff --git a/src/test/run-pass/specialization-projection-alias.rs b/src/test/run-pass/specialization/specialization-projection-alias.rs similarity index 100% rename from src/test/run-pass/specialization-projection-alias.rs rename to src/test/run-pass/specialization/specialization-projection-alias.rs diff --git a/src/test/run-pass/specialization-projection.rs b/src/test/run-pass/specialization/specialization-projection.rs similarity index 100% rename from src/test/run-pass/specialization-projection.rs rename to src/test/run-pass/specialization/specialization-projection.rs diff --git a/src/test/run-pass/specialization-super-traits.rs b/src/test/run-pass/specialization/specialization-super-traits.rs similarity index 100% rename from src/test/run-pass/specialization-super-traits.rs rename to src/test/run-pass/specialization/specialization-super-traits.rs diff --git a/src/test/run-pass/specialization-translate-projections-with-params.rs b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs similarity index 100% rename from src/test/run-pass/specialization-translate-projections-with-params.rs rename to src/test/run-pass/specialization/specialization-translate-projections-with-params.rs diff --git a/src/test/run-pass/specialization-translate-projections.rs b/src/test/run-pass/specialization/specialization-translate-projections.rs similarity index 100% rename from src/test/run-pass/specialization-translate-projections.rs rename to src/test/run-pass/specialization/specialization-translate-projections.rs From d80189d305ce24b6a1fd83a4f724d17506b64a13 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 11 Mar 2016 12:15:28 -0800 Subject: [PATCH 32/37] Test fixes, added README for tests --- src/librustc/middle/traits/specialize/mod.rs | 17 ++++++--- .../xcrate_associated_type_defaults.rs | 12 ++++-- .../compile-fail/specialization/README.md | 21 +++++++++++ .../specialization-default-projection.rs | 4 +- .../specialization-default-types.rs | 4 +- .../specialization/specialization-overlap.rs | 4 ++ src/test/run-pass/default-associated-types.rs | 21 +++++------ src/test/run-pass/specialization/README.md | 37 +++++++++++++++++++ .../specialization-assoc-fns.rs | 2 + .../specialization-cross-crate-defaults.rs | 12 ++++++ .../specialization-cross-crate-no-gate.rs | 29 +++++++++++++++ .../specialization-default-methods.rs | 2 + .../specialization-on-projection.rs | 2 + .../specialization-overlap-projection.rs | 33 +++++++++++++++++ .../specialization-projection-alias.rs | 6 ++- .../specialization-projection.rs | 22 ++++++++++- ...ation-translate-projections-with-params.rs | 4 ++ .../specialization-translate-projections.rs | 3 ++ .../xcrate-associated-type-defaults.rs | 22 ++++++++++- 19 files changed, 230 insertions(+), 27 deletions(-) create mode 100644 src/test/compile-fail/specialization/README.md create mode 100644 src/test/run-pass/specialization/README.md create mode 100644 src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs create mode 100644 src/test/run-pass/specialization/specialization-overlap-projection.rs diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index a5b3c667fc06d..624ebc545fe7c 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -25,7 +25,7 @@ use middle::def_id::DefId; use middle::infer::{self, InferCtxt, TypeOrigin}; use middle::region; use middle::subst::{Subst, Substs}; -use middle::traits::ProjectionMode; +use middle::traits::{self, ProjectionMode, ObligationCause, Normalized}; use middle::ty::{self, TyCtxt}; use syntax::codemap::DUMMY_SP; @@ -149,14 +149,21 @@ pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bo // create a parameter environment corresponding to a (skolemized) instantiation of impl1 let scheme = tcx.lookup_item_type(impl1_def_id); let predicates = tcx.lookup_predicates(impl1_def_id); - let penv = tcx.construct_parameter_environment(DUMMY_SP, - &scheme.generics, - &predicates, - region::DUMMY_CODE_EXTENT); + let mut penv = tcx.construct_parameter_environment(DUMMY_SP, + &scheme.generics, + &predicates, + region::DUMMY_CODE_EXTENT); let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id) .unwrap() .subst(tcx, &penv.free_substs); + // Normalize the trait reference, adding any obligations that arise into the impl1 assumptions + let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = { + let selcx = &mut SelectionContext::new(&infcx); + traits::normalize(selcx, ObligationCause::dummy(), &impl1_trait_ref) + }; + penv.caller_bounds.extend(normalization_obligations.into_iter().map(|o| o.predicate)); + // Install the parameter environment, which means we take the predicates of impl1 as assumptions: infcx.parameter_environment = penv; diff --git a/src/test/auxiliary/xcrate_associated_type_defaults.rs b/src/test/auxiliary/xcrate_associated_type_defaults.rs index 43852a4e793f3..6779438c67226 100644 --- a/src/test/auxiliary/xcrate_associated_type_defaults.rs +++ b/src/test/auxiliary/xcrate_associated_type_defaults.rs @@ -10,9 +10,13 @@ #![feature(associated_type_defaults)] -pub trait Foo { - type Input = usize; - fn bar(&self, _: Self::Input) {} +pub trait Foo { + type Out: Default + ToString = T; } -impl Foo for () {} +impl Foo for () { +} + +impl Foo for () { + type Out = bool; +} diff --git a/src/test/compile-fail/specialization/README.md b/src/test/compile-fail/specialization/README.md new file mode 100644 index 0000000000000..f2b4bf946c537 --- /dev/null +++ b/src/test/compile-fail/specialization/README.md @@ -0,0 +1,21 @@ +This directory contains the test for incorrect usage of specialization that +should lead to compile failure. Those tests break down into a few categories: + +- Feature gating + - [On use of the `default` keyword](specialization-feature-gate-default.rs) + - [On overlapping impls](specialization-feature-gate-overlap.rs) + +- Overlap checking with specialization enabled + - [Basic overlap scenarios](specialization-overlap.rs) + - Includes purely structural overlap + - Includes purely trait-based overlap + - Includes mix + - [Overlap with differing polarity](specialization-overlap-negative.rs) + +- [Attempt to specialize without using `default`](specialization-no-default.rs) + +- [Attempt to change impl polarity in a specialization](specialization-polarity.rs) + +- Attempt to rely on projection of a `default` type + - [Rely on it externally in both generic and monomorphic contexts](specialization-default-projection.rs) + - [Rely on it both within an impl and outside it](specialization-default-types.rs) diff --git a/src/test/compile-fail/specialization/specialization-default-projection.rs b/src/test/compile-fail/specialization/specialization-default-projection.rs index 377838f2a080c..96cbd7a485251 100644 --- a/src/test/compile-fail/specialization/specialization-default-projection.rs +++ b/src/test/compile-fail/specialization/specialization-default-projection.rs @@ -28,14 +28,14 @@ fn generic() -> ::Assoc { // `T` could be some downstream crate type that specializes (or, // for that matter, `u8`). - () //~ ERROR E0308 + () //~ ERROR mismatched types } fn monomorphic() -> () { // Even though we know that `()` is not specialized in a // downstream crate, typeck refuses to project here. - generic::<()>() //~ ERROR E0308 + generic::<()>() //~ ERROR mismatched types } fn main() { diff --git a/src/test/compile-fail/specialization/specialization-default-types.rs b/src/test/compile-fail/specialization/specialization-default-types.rs index 3c2e3d5a36c37..18acecb42296a 100644 --- a/src/test/compile-fail/specialization/specialization-default-types.rs +++ b/src/test/compile-fail/specialization/specialization-default-types.rs @@ -22,7 +22,7 @@ trait Example { impl Example for T { default type Output = Box; default fn generate(self) -> Self::Output { - Box::new(self) //~ ERROR E0308 + Box::new(self) //~ ERROR mismatched types } } @@ -32,7 +32,7 @@ impl Example for bool { } fn trouble(t: T) -> Box { - Example::generate(t) //~ ERROR E0308 + Example::generate(t) //~ ERROR mismatched types } fn weaponize() -> bool { diff --git a/src/test/compile-fail/specialization/specialization-overlap.rs b/src/test/compile-fail/specialization/specialization-overlap.rs index 57529d2ae4266..f579817100107 100644 --- a/src/test/compile-fail/specialization/specialization-overlap.rs +++ b/src/test/compile-fail/specialization/specialization-overlap.rs @@ -22,4 +22,8 @@ trait Baz {} impl Baz for u8 {} impl Baz for T {} //~ ERROR E0119 +trait Qux {} +impl Qux for T {} +impl Qux for T {} //~ ERROR E0119 + fn main() {} diff --git a/src/test/run-pass/default-associated-types.rs b/src/test/run-pass/default-associated-types.rs index 3e6c72c993a0a..ed55d5c8b171e 100644 --- a/src/test/run-pass/default-associated-types.rs +++ b/src/test/run-pass/default-associated-types.rs @@ -10,23 +10,22 @@ #![feature(associated_type_defaults)] -trait Foo { - type Out = T; - fn foo(&self) -> Self::Out; +trait Foo { + type Out: Default + ToString = T; } impl Foo for () { - fn foo(&self) -> u32 { - 4u32 - } } -impl Foo for bool { - type Out = (); - fn foo(&self) {} +impl Foo for () { + type Out = bool; } fn main() { - assert_eq!(<() as Foo>::foo(&()), 4u32); - assert_eq!(>::foo(&true), ()); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "0"); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "false"); } diff --git a/src/test/run-pass/specialization/README.md b/src/test/run-pass/specialization/README.md new file mode 100644 index 0000000000000..1373a2cf81b3a --- /dev/null +++ b/src/test/run-pass/specialization/README.md @@ -0,0 +1,37 @@ +Tests that specialization is working correctly: + +- Dispatch + - [On methods](specialization-basics.rs), includes: + - Specialization via adding a trait bound + - Including both remote and local traits + - Specialization via pure structure (e.g. `(T, U)` vs `(T, T)`) + - Specialization via concrete types vs unknown types + - In top level of the trait reference + - Embedded within another type (`Vec` vs `Vec`) + - [Specialization based on super trait relationships](specialization-super-traits.rs) + - [On assoc fns](specialization-assoc-fns.rs) + - [Ensure that impl order doesn't matter](specialization-out-of-order.rs) + +- Item inheritance + - [Correct default cascading for methods](specialization-default-methods.rs) + - Inheritance works across impls with varying generics + - [With projections](specialization-translate-projections.rs) + - [With projections that involve input types](specialization-translate-projections-with-params.rs) + +- Normalization issues + - [Non-default assoc types can be projected](specialization-projection.rs) + - Including non-specialized cases + - Including specialized cases + - [Specialized Impls can happen on projections](specialization-on-projection.rs) + - [Projections and aliases play well together](specialization-projection-alias.rs) + - [Projections involving specialization allowed in the trait ref for impls, and overlap can still be determined](specialization-overlap-projection.rs) + - Only works for the simple case where the most specialized impl directly + provides a non-`default` associated type + +- Across crates + - [For traits defined in upstream crate](specialization-allowed-cross-crate.rs) + - [Full method dispatch tests, drawing from upstream crate](specialization-cross-crate.rs) + - Including *additional* local specializations + - [Full method dispatch tests, *without* turning on specialization in local crate](specialization-cross-crate-no-gate.rs) + - [Test that defaults cascade correctly from upstream crates](specialization-cross-crate-defaults.rs) + - Including *additional* local use of defaults diff --git a/src/test/run-pass/specialization/specialization-assoc-fns.rs b/src/test/run-pass/specialization/specialization-assoc-fns.rs index 683e0d55dc944..577f217862da3 100644 --- a/src/test/run-pass/specialization/specialization-assoc-fns.rs +++ b/src/test/run-pass/specialization/specialization-assoc-fns.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that non-method associated functions can be specialized + #![feature(specialization)] trait Foo { diff --git a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs index 750c3cf8b3ecb..bc695ea821d0a 100644 --- a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs +++ b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs @@ -16,10 +16,22 @@ extern crate specialization_cross_crate_defaults; use specialization_cross_crate_defaults::*; +struct LocalDefault; +struct LocalOverride; + +impl Foo for LocalDefault {} + +impl Foo for LocalOverride { + fn foo(&self) -> bool { true } +} + fn test_foo() { assert!(0i8.foo() == false); assert!(0i32.foo() == false); assert!(0i64.foo() == true); + + assert!(LocalDefault.foo() == false); + assert!(LocalOverride.foo() == true); } fn test_bar() { diff --git a/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs b/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs new file mode 100644 index 0000000000000..b9548539e1649 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs @@ -0,0 +1,29 @@ +// 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. + +// Test that specialization works even if only the upstream crate enables it + +// aux-build:specialization_cross_crate.rs + +extern crate specialization_cross_crate; + +use specialization_cross_crate::*; + +fn main() { + assert!(0u8.foo() == "generic Clone"); + assert!(vec![0u8].foo() == "generic Vec"); + assert!(vec![0i32].foo() == "Vec"); + assert!(0i32.foo() == "i32"); + assert!(String::new().foo() == "String"); + assert!(((), 0).foo() == "generic pair"); + assert!(((), ()).foo() == "generic uniform pair"); + assert!((0u8, 0u32).foo() == "(u8, u32)"); + assert!((0u8, 0u8).foo() == "(u8, u8)"); +} diff --git a/src/test/run-pass/specialization/specialization-default-methods.rs b/src/test/run-pass/specialization/specialization-default-methods.rs index d662c5bfa28d6..b2fad9d171f9d 100644 --- a/src/test/run-pass/specialization/specialization-default-methods.rs +++ b/src/test/run-pass/specialization/specialization-default-methods.rs @@ -10,6 +10,8 @@ #![feature(specialization)] +// Test that default methods are cascaded correctly + // First, test only use of explicit `default` items: trait Foo { diff --git a/src/test/run-pass/specialization/specialization-on-projection.rs b/src/test/run-pass/specialization/specialization-on-projection.rs index 65cbb31d22147..acf78def1b967 100644 --- a/src/test/run-pass/specialization/specialization-on-projection.rs +++ b/src/test/run-pass/specialization/specialization-on-projection.rs @@ -10,6 +10,8 @@ #![feature(specialization)] +// Ensure that specialization works for impls defined directly on a projection + trait Foo {} trait Assoc { diff --git a/src/test/run-pass/specialization/specialization-overlap-projection.rs b/src/test/run-pass/specialization/specialization-overlap-projection.rs new file mode 100644 index 0000000000000..20046ee66b0e2 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-overlap-projection.rs @@ -0,0 +1,33 @@ +// Copyright 2016 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. + +// Test that impls on projected self types can resolve overlap, even when the +// projections involve specialization, so long as the associated type is +// provided by the most specialized impl. + +#![feature(specialization)] + +trait Assoc { + type Output; +} + +impl Assoc for T { + default type Output = bool; +} + +impl Assoc for u8 { type Output = u8; } +impl Assoc for u16 { type Output = u16; } + +trait Foo {} +impl Foo for u32 {} +impl Foo for ::Output {} +impl Foo for ::Output {} + +fn main() {} diff --git a/src/test/run-pass/specialization/specialization-projection-alias.rs b/src/test/run-pass/specialization/specialization-projection-alias.rs index 2250d77e08e8e..7fce1cca582c1 100644 --- a/src/test/run-pass/specialization/specialization-projection-alias.rs +++ b/src/test/run-pass/specialization/specialization-projection-alias.rs @@ -23,6 +23,10 @@ impl Id_ for T { default type Out = T; } -fn main() { +fn test_proection() { let x: Id = panic!(); } + +fn main() { + +} diff --git a/src/test/run-pass/specialization/specialization-projection.rs b/src/test/run-pass/specialization/specialization-projection.rs index d26d59896a574..4e0bdec297fe2 100644 --- a/src/test/run-pass/specialization/specialization-projection.rs +++ b/src/test/run-pass/specialization/specialization-projection.rs @@ -13,6 +13,8 @@ // Make sure we *can* project non-defaulted associated types // cf compile-fail/specialization-default-projection.rs +// First, do so without any use of specialization + trait Foo { type Assoc; } @@ -21,9 +23,27 @@ impl Foo for T { type Assoc = (); } -fn generic() -> ::Assoc { +fn generic_foo() -> ::Assoc { () } +// Next, allow for one layer of specialization + +trait Bar { + type Assoc; +} + +impl Bar for T { + default type Assoc = (); +} + +impl Bar for T { + type Assoc = u8; +} + +fn generic_bar_clone() -> ::Assoc { + 0u8 +} + fn main() { } diff --git a/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs index 3d90bc56f42bd..647d5523c376c 100644 --- a/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs +++ b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs @@ -8,6 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Ensure that provided items are inherited properly even when impls vary in +// type parameters *and* rely on projections, and the type parameters are input +// types on the trait. + #![feature(specialization)] trait Trait { diff --git a/src/test/run-pass/specialization/specialization-translate-projections.rs b/src/test/run-pass/specialization/specialization-translate-projections.rs index d224efe8c3183..11e1d997fdda0 100644 --- a/src/test/run-pass/specialization/specialization-translate-projections.rs +++ b/src/test/run-pass/specialization/specialization-translate-projections.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Ensure that provided items are inherited properly even when impls vary in +// type parameters *and* rely on projections. + #![feature(specialization)] use std::convert::Into; diff --git a/src/test/run-pass/xcrate-associated-type-defaults.rs b/src/test/run-pass/xcrate-associated-type-defaults.rs index 1b6de3b2f7bcf..2dacbe0966ee3 100644 --- a/src/test/run-pass/xcrate-associated-type-defaults.rs +++ b/src/test/run-pass/xcrate-associated-type-defaults.rs @@ -13,6 +13,26 @@ extern crate xcrate_associated_type_defaults; use xcrate_associated_type_defaults::Foo; +struct LocalDefault; +impl Foo for LocalDefault {} + +struct LocalOverride; +impl Foo for LocalOverride { + type Out = bool; +} + fn main() { - ().bar(5); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "0"); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "false"); + + assert_eq!( + >::Out::default().to_string(), + "0"); + assert_eq!( + >::Out::default().to_string(), + "false"); } From e36620dd9c29e39da688d87e1a6566710aa2ccc5 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 11 Mar 2016 14:47:29 -0800 Subject: [PATCH 33/37] Introduce ICE when the topmost projection restriction kicks in, as per issue #32205 --- src/librustc/middle/traits/project.rs | 33 ++++++++++++++++--- .../compile-fail/private-in-public-warn.rs | 8 +++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 0c857692ad11c..469ce0a4d2799 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -38,13 +38,13 @@ use std::rc::Rc; /// more or less conservative. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ProjectionMode { + /// FIXME (#32205) /// At coherence-checking time, we're still constructing the /// specialization graph, and thus we only project project /// non-`default` associated types that are defined directly in /// the applicable impl. (This behavior should be improved over /// time, to allow for successful projections modulo cycles /// between different impls). - // TODO: Add tracking issue to do better here. /// /// Here's an example that will fail due to the restriction: /// @@ -66,7 +66,6 @@ pub enum ProjectionMode { /// /// The projection would succeed if `Output` had been defined /// directly in the impl for `u8`. - // TODO: Add test Topmost, /// At type-checking time, we refuse to project any associated @@ -91,7 +90,6 @@ pub enum ProjectionMode { /// fn main() { /// let <() as Assoc>::Output = true; /// } - // TODO: Add test AnyFinal, /// At trans time, all projections will succeed. @@ -695,7 +693,34 @@ fn project_type<'cx,'tcx>( // at the topmost impl (we don't even consider the trait // itself) for the definition -- so we can fail to find a // definition of the type even if it exists. - return None; + + // For now, we just unconditionally ICE, because otherwise, + // examples like the following will succeed: + // + // ``` + // trait Assoc { + // type Output; + // } + // + // impl Assoc for T { + // default type Output = bool; + // } + // + // impl Assoc for u8 {} + // impl Assoc for u16 {} + // + // trait Foo {} + // impl Foo for ::Output {} + // impl Foo for ::Output {} + // return None; + // } + // ``` + // + // The essential problem here is that the projection fails, + // leaving two unnormalized types, which appear not to unify + // -- so the overlap check succeeds, when it should fail. + selcx.tcx().sess.bug("Tried to project an inherited associated type during \ + coherence checking, which is currently not supported."); } } } diff --git a/src/test/compile-fail/private-in-public-warn.rs b/src/test/compile-fail/private-in-public-warn.rs index 9aab06ce14ee1..b9d632a8cf07e 100644 --- a/src/test/compile-fail/private-in-public-warn.rs +++ b/src/test/compile-fail/private-in-public-warn.rs @@ -198,9 +198,11 @@ mod aliases_pub { use self::m::PubTr as PrivUseAliasTr; type PrivAlias = m::Pub2; trait PrivTr { + type AssocAlias; + } + impl PrivTr for Priv { type AssocAlias = m::Pub3; } - impl PrivTr for Priv {} pub fn f1(arg: PrivUseAlias) {} // OK @@ -245,9 +247,11 @@ mod aliases_priv { use self::PrivTr1 as PrivUseAliasTr; type PrivAlias = Priv2; trait PrivTr { + type AssocAlias; + } + impl PrivTr for Priv { type AssocAlias = Priv3; } - impl PrivTr for Priv {} pub trait Tr1: PrivUseAliasTr {} //~ WARN private trait in public interface //~^ WARNING hard error From e5753b4605009260c0055a3bbb1efe4c06728650 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 11 Mar 2016 15:51:35 -0800 Subject: [PATCH 34/37] Fixes after rebase --- src/librustc/middle/traits/coherence.rs | 1 - src/librustc/middle/traits/mod.rs | 5 ++++- src/librustc/middle/traits/project.rs | 6 ++++-- src/librustc/middle/traits/specialize/mod.rs | 10 +++++----- .../traits/specialize/specialization_graph.rs | 4 ++-- src/librustc_driver/test.rs | 6 +++++- src/librustc_mir/transform/type_check.rs | 7 +++++-- src/librustc_trans/trans/collector.rs | 8 ++++---- src/librustc_trans/trans/meth.rs | 13 ++++++------- src/librustc_typeck/coherence/overlap.rs | 13 ++++++++++--- src/librustc_typeck/collect.rs | 3 ++- 11 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 33a1e3816e348..64d1c992cbf1d 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -11,7 +11,6 @@ //! See `README.md` for high-level documentation use super::{SelectionContext, Obligation, ObligationCause}; -use super::util; use middle::cstore::LOCAL_CRATE; use middle::def_id::DefId; diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 76dbd2793f9f6..5f66e9e6344ad 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -433,7 +433,10 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi let elaborated_env = unnormalized_env.with_caller_bounds(predicates); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env), ProjectionMode::AnyFinal); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(elaborated_env), + ProjectionMode::AnyFinal); let predicates = match fully_normalize(&infcx, cause, &infcx.parameter_environment.caller_bounds) { diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 469ce0a4d2799..e86f3ed01a49d 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -1121,7 +1121,7 @@ fn confirm_impl_candidate<'cx,'tcx>( tcx.types.err }); let substs = translate_substs(selcx.infcx(), impl_def_id, substs, node_item.node); - (ty.subst(tcx, &substs), nested) + (ty.subst(tcx, substs), nested) } None => { tcx.sess.span_bug(obligation.cause.span, @@ -1135,7 +1135,9 @@ fn confirm_impl_candidate<'cx,'tcx>( /// /// Based on the "projection mode", this lookup may in fact only examine the /// topmost impl. See the comments for `ProjectionMode` for more details. -fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>, impl_def_id: DefId, assoc_ty_name: ast::Name) +fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>, + impl_def_id: DefId, + assoc_ty_name: ast::Name) -> Option>>> { let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id; diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index 624ebc545fe7c..a692fe55a7789 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -75,9 +75,9 @@ pub struct Overlap<'a, 'tcx: 'a> { /// resolved. pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, source_impl: DefId, - source_substs: Substs<'tcx>, + source_substs: &'tcx Substs<'tcx>, target_node: specialization_graph::Node) - -> Substs<'tcx> { + -> &'tcx Substs<'tcx> { let source_trait_ref = infcx.tcx .impl_trait_ref(source_impl) .unwrap() @@ -111,7 +111,7 @@ pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, }; // directly inherent the method generics, since those do not vary across impls - target_substs.with_method_from_subst(&source_substs) + infcx.tcx.mk_substs(target_substs.with_method_from_subst(source_substs)) } /// Is impl1 a specialization of impl2? @@ -164,7 +164,7 @@ pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bo }; penv.caller_bounds.extend(normalization_obligations.into_iter().map(|o| o.predicate)); - // Install the parameter environment, which means we take the predicates of impl1 as assumptions: + // Install the parameter environment, taking the predicates of impl1 as assumptions: infcx.parameter_environment = penv; // Attempt to prove that impl2 applies, given all of the above. @@ -217,7 +217,7 @@ fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, infcx.parameter_environment.caller_bounds); Err(()) } else { - debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", + debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait_ref, target_trait_ref); diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs index 4af8e1fe0a831..f2170f75a11fe 100644 --- a/src/librustc/middle/traits/specialize/specialization_graph.rs +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -97,7 +97,7 @@ impl Graph { let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost); let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); - if let Some(trait_ref) = overlap { + if let Some(impl_header) = overlap { let le = specializes(tcx, impl_def_id, possible_sibling); let ge = specializes(tcx, possible_sibling, impl_def_id); @@ -124,7 +124,7 @@ impl Graph { // overlap, but no specialization; error out return Err(Overlap { with_impl: possible_sibling, - on_trait_ref: trait_ref, + on_trait_ref: impl_header.trait_ref.unwrap(), in_context: infcx, }); } diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 3cab9cfb88ca9..437672c551464 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -22,6 +22,7 @@ use rustc_typeck::middle::resolve_lifetime; use rustc_typeck::middle::stability; use rustc_typeck::middle::subst; use rustc_typeck::middle::subst::Subst; +use rustc_typeck::middle::traits::ProjectionMode; use rustc_typeck::middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_typeck::middle::ty::relate::TypeRelation; use rustc_typeck::middle::infer::{self, TypeOrigin}; @@ -143,7 +144,10 @@ fn test_env(source_string: &str, lang_items, index, |tcx| { - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + None, + ProjectionMode::AnyFinal); body(Env { infcx: &infcx }); let free_regions = FreeRegionMap::new(); infcx.resolve_regions_and_report_errors(&free_regions, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 45393d57101e5..d99e6ff4bf55d 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -13,7 +13,7 @@ use rustc::dep_graph::DepNode; use rustc::middle::infer::{self, InferCtxt}; -use rustc::middle::traits; +use rustc::middle::traits::{self, ProjectionMode}; use rustc::middle::ty::fold::TypeFoldable; use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::mir::repr::*; @@ -582,7 +582,10 @@ impl<'tcx> MirPass<'tcx> for TypeckMir { } let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id)); let param_env = ty::ParameterEnvironment::for_item(tcx, id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); let mut checker = TypeChecker::new(&infcx); { let mut verifier = TypeVerifier::new(&mut checker, mir); diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs index b7e107ae5e783..cea97c1a1e77f 100644 --- a/src/librustc_trans/trans/collector.rs +++ b/src/librustc_trans/trans/collector.rs @@ -823,7 +823,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, impl_did, tcx.mk_substs(callee_substs), trait_method.name); - Some((impl_method.method.def_id, impl_method.substs)) + Some((impl_method.method.def_id, &impl_method.substs)) } // If we have a closure or a function pointer, we will also encounter // the concrete closure/function somewhere else (during closure or fn @@ -983,7 +983,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if can_have_local_instance(ccx, impl_method.method.def_id) { Some(create_fn_trans_item(ccx, impl_method.method.def_id, - impl_method.substs, + &impl_method.substs, &Substs::trans_empty())) } else { None @@ -1163,12 +1163,12 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // the method type from the impl to substitute into. let mth = meth::get_impl_method(tcx, impl_def_id, - callee_substs.clone(), + callee_substs, default_impl.name); assert!(mth.is_provided); - let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs); + let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); if !normalize_and_test_predicates(ccx, predicates.into_vec()) { continue; } diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 2965e44bc1748..7397ccc2505f1 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -34,7 +34,6 @@ use trans::machine; use trans::type_::Type; use trans::type_of::*; use middle::ty::{self, Ty, TyCtxt, TypeFoldable}; -use middle::ty::MethodCall; use syntax::ast::{self, Name}; use syntax::attr; @@ -110,7 +109,7 @@ pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // those from the impl and those from the method: let impl_substs = vtable_impl.substs.with_method_from(&substs); let substs = ccx.tcx().mk_substs(impl_substs); - let mth = get_impl_method(ccx.tcx(), impl_did, impl_substs, mname); + let mth = get_impl_method(ccx.tcx(), impl_did, substs, mname); // Translate the function, bypassing Callee::def. // That is because default methods have the same ID as the @@ -318,7 +317,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, trans_fn_ref_with_substs(ccx, mth.method.def_id, None, - mth.substs).val + &mth.substs).val } None => nullptr } @@ -431,7 +430,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let mth = get_impl_method(tcx, impl_id, substs.clone(), name); + let mth = get_impl_method(tcx, impl_id, substs, name); debug!("get_vtable_methods: mth={:?}", mth); @@ -441,7 +440,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // method could then never be called, so we do not want to // try and trans it, in that case. Issue #23435. if mth.is_provided { - let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs); + let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); if !normalize_and_test_predicates(ccx, predicates.into_vec()) { debug!("get_vtable_methods: predicates do not hold"); return None; @@ -473,14 +472,14 @@ fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>) #[derive(Debug)] pub struct ImplMethod<'tcx> { pub method: Rc>, - pub substs: Substs<'tcx>, + pub substs: &'tcx Substs<'tcx>, pub is_provided: bool } /// Locates the applicable definition of a method, given its name. pub fn get_impl_method<'tcx>(tcx: &TyCtxt<'tcx>, impl_def_id: DefId, - substs: Substs<'tcx>, + substs: &'tcx Substs<'tcx>, name: Name) -> ImplMethod<'tcx> { diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index e4994eaa25528..99dd72538c986 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -13,9 +13,12 @@ //! constructor provide a method with the same name. use middle::cstore::CrateStore; -use middle::traits; +use middle::def_id::DefId; +use middle::traits::{self, ProjectionMode}; +use middle::infer; use middle::ty::{self, TyCtxt}; use syntax::ast; +use syntax::codemap::Span; use rustc::dep_graph::DepNode; use rustc_front::hir; use rustc_front::intravisit; @@ -86,7 +89,10 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { for (i, &impl1_def_id) in impls.iter().enumerate() { for &impl2_def_id in &impls[(i+1)..] { - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + None, + ProjectionMode::Topmost); if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() { self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id) } @@ -117,7 +123,8 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { self.tcx.span_of_impl(impl_def_id).unwrap(), E0519, "redundant default implementations of trait `{}`:", trait_ref); - err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id)).unwrap(), + err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id)) + .unwrap(), "redundant implementation is here:"); err.emit(); } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 831c9804d1b0d..0f88640b62951 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -804,7 +804,8 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_method(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, method_vis, - sig, impl_item.defaultness, selfty, &ty_generics, &ty_predicates); + sig, impl_item.defaultness, selfty, &ty_generics, + &ty_predicates); } } From c4f78ad7bfbb4cc4393901ccc9eb8a68e1137022 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 13 Mar 2016 13:30:44 -0700 Subject: [PATCH 35/37] Parse fail test fixes --- src/test/parse-fail/issue-20711-2.rs | 2 +- src/test/parse-fail/issue-20711.rs | 2 +- src/test/parse-fail/removed-syntax-static-fn.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/parse-fail/issue-20711-2.rs b/src/test/parse-fail/issue-20711-2.rs index be6bd516d6fe4..a489864e3f737 100644 --- a/src/test/parse-fail/issue-20711-2.rs +++ b/src/test/parse-fail/issue-20711-2.rs @@ -16,6 +16,6 @@ impl Foo { fn foo() {} #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` +} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` fn main() {} diff --git a/src/test/parse-fail/issue-20711.rs b/src/test/parse-fail/issue-20711.rs index d1d8d3acf9187..d9789d55a6faf 100644 --- a/src/test/parse-fail/issue-20711.rs +++ b/src/test/parse-fail/issue-20711.rs @@ -14,6 +14,6 @@ struct Foo; impl Foo { #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` +} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` fn main() {} diff --git a/src/test/parse-fail/removed-syntax-static-fn.rs b/src/test/parse-fail/removed-syntax-static-fn.rs index 7b6caad86b6cc..b4c25a75c9086 100644 --- a/src/test/parse-fail/removed-syntax-static-fn.rs +++ b/src/test/parse-fail/removed-syntax-static-fn.rs @@ -15,4 +15,4 @@ struct S; impl S { static fn f() {} } -//~^^ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static` +//~^^ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}` From dc45d924b6ec0ffc7fadf8763ce5fc91946e2dcd Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 13 Mar 2016 14:18:45 -0700 Subject: [PATCH 36/37] Adjust error code --- src/librustc_typeck/coherence/overlap.rs | 2 +- src/librustc_typeck/diagnostics.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 99dd72538c986..f7fa3e1b1429a 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -120,7 +120,7 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { if let Some(prev_id) = prev_default_impl { let mut err = struct_span_err!( self.tcx.sess, - self.tcx.span_of_impl(impl_def_id).unwrap(), E0519, + self.tcx.span_of_impl(impl_def_id).unwrap(), E0521, "redundant default implementations of trait `{}`:", trait_ref); err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id)) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 79508c2ca9f8d..19cfc13ea615c 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3696,6 +3696,6 @@ register_diagnostics! { // type `{}` was overridden E0436, // functional record update requires a struct E0513, // no type for local variable .. - E0519, // redundant default implementations of trait - E0520 // cannot specialize non-default item + E0520, // cannot specialize non-default item + E0521 // redundant default implementations of trait } From 6562eeb05396be4ee4d4f413ac3e0f9f1afcc536 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 13 Mar 2016 22:55:26 -0700 Subject: [PATCH 37/37] Add pretty printer output for `default` --- src/librustc_front/print/pprust.rs | 5 +++++ src/libsyntax/print/pprust.rs | 3 +++ .../specialization/specialization-default-methods.rs | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs index 143dfce09b602..1100f084454c7 100644 --- a/src/librustc_front/print/pprust.rs +++ b/src/librustc_front/print/pprust.rs @@ -1014,6 +1014,11 @@ impl<'a> State<'a> { try!(self.hardbreak_if_not_bol()); try!(self.maybe_print_comment(ii.span.lo)); try!(self.print_outer_attributes(&ii.attrs)); + + if let hir::Defaultness::Default = ii.defaultness { + try!(self.word_nbsp("default")); + } + match ii.node { hir::ImplItemKind::Const(ref ty, ref expr) => { try!(self.print_associated_const(ii.name, &ty, Some(&expr), ii.vis)); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 2cfed1f82f7ec..533487ae1c547 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1582,6 +1582,9 @@ impl<'a> State<'a> { try!(self.hardbreak_if_not_bol()); try!(self.maybe_print_comment(ii.span.lo)); try!(self.print_outer_attributes(&ii.attrs)); + if let ast::Defaultness::Default = ii.defaultness { + try!(self.word_nbsp("default")); + } match ii.node { ast::ImplItemKind::Const(ref ty, ref expr) => { try!(self.print_associated_const(ii.ident, &ty, Some(&expr), ii.vis)); diff --git a/src/test/run-pass/specialization/specialization-default-methods.rs b/src/test/run-pass/specialization/specialization-default-methods.rs index b2fad9d171f9d..3f0f21ff03f27 100644 --- a/src/test/run-pass/specialization/specialization-default-methods.rs +++ b/src/test/run-pass/specialization/specialization-default-methods.rs @@ -63,7 +63,8 @@ trait Bar { // / \ // Vec $Vec -impl Bar for T {} // use the provided method +// use the provided method +impl Bar for T {} impl Bar for i32 { fn bar(&self) -> i32 { 1 }