From d85ff16173b54a1aa813eabb411c45c28ffcb66d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Nov 2014 16:14:32 -0500 Subject: [PATCH 1/3] Treat builtin bounds like all other kinds of trait matches. Introduce a simple hashset in the fulfillment context to catch cases where we register the exact same obligation twice. This helps prevent duplicate error reports but also handles the recursive obligations created by builtin bounds. --- src/librustc/middle/traits/fulfill.rs | 20 +++++++++++++--- src/librustc/middle/traits/select.rs | 34 ++++++++++----------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index bf4273dbfd0..25c86be993f 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -11,6 +11,8 @@ use middle::mem_categorization::Typer; use middle::ty; use middle::typeck::infer::InferCtxt; +use std::collections::HashSet; +use std::rc::Rc; use util::ppaux::Repr; use super::CodeAmbiguity; @@ -30,6 +32,13 @@ use super::select::SelectionContext; /// method `select_all_or_error` can be used to report any remaining /// ambiguous cases as errors. pub struct FulfillmentContext<'tcx> { + // a simple cache that aims to cache *exact duplicate obligations* + // and avoid adding them twice. This serves a different purpose + // than the `SelectionCache`: it avoids duplicate errors and + // permits recursive obligations, which are often generated from + // traits like `Send` et al. + duplicate_set: HashSet>>, + // A list of all obligations that have been registered with this // fulfillment context. trait_obligations: Vec>, @@ -43,6 +52,7 @@ pub struct FulfillmentContext<'tcx> { impl<'tcx> FulfillmentContext<'tcx> { pub fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { + duplicate_set: HashSet::new(), trait_obligations: Vec::new(), attempted_mark: 0, } @@ -52,9 +62,13 @@ impl<'tcx> FulfillmentContext<'tcx> { tcx: &ty::ctxt<'tcx>, obligation: Obligation<'tcx>) { - debug!("register_obligation({})", obligation.repr(tcx)); - assert!(!obligation.trait_ref.has_escaping_regions()); - self.trait_obligations.push(obligation); + if self.duplicate_set.insert(obligation.trait_ref.clone()) { + debug!("register_obligation({})", obligation.repr(tcx)); + assert!(!obligation.trait_ref.has_escaping_regions()); + self.trait_obligations.push(obligation); + } else { + debug!("register_obligation({}) -- already seen, skip", obligation.repr(tcx)); + } } pub fn select_all_or_error<'a>(&mut self, diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 9ce6947731d..71a183b475c 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -113,7 +113,7 @@ pub enum MethodMatchedData { /// candidate is one that might match or might not, depending on how /// type variables wind up being resolved. This only occurs during inference. /// -/// For selection to suceed, there must be exactly one non-ambiguous +/// For selection to succeed, there must be exactly one non-ambiguous /// candidate. Usually, it is not possible to have more than one /// definitive candidate, due to the coherence rules. However, there is /// one case where it could occur: if there is a blanket impl for a @@ -1149,24 +1149,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut CandidateSet<'tcx>) -> Result<(),SelectionError<'tcx>> { - // FIXME -- To be more like a normal impl, we should just - // ignore the nested cases here, and instead generate nested - // obligations in `confirm_candidate`. However, this doesn't - // work because we require handling the recursive cases to - // avoid infinite cycles (that is, with recursive types, - // sometimes `Foo : Copy` only holds if `Foo : Copy`). - match self.builtin_bound(bound, stack.obligation.self_ty()) { - Ok(If(nested)) => { - debug!("builtin_bound: bound={} nested={}", - bound.repr(self.tcx()), - nested.repr(self.tcx())); - let data = self.vtable_builtin_data(stack.obligation, bound, nested); - match self.winnow_selection(Some(stack), VtableBuiltin(data)) { - EvaluatedToOk => { Ok(candidates.vec.push(BuiltinCandidate(bound))) } - EvaluatedToAmbig => { Ok(candidates.ambiguous = true) } - EvaluatedToErr => { Err(Unimplemented) } - } + Ok(If(_)) => { + debug!("builtin_bound: bound={}", + bound.repr(self.tcx())); + candidates.vec.push(BuiltinCandidate(bound)); + Ok(()) } Ok(ParameterBuiltin) => { Ok(()) } Ok(AmbiguousBuiltin) => { Ok(candidates.ambiguous = true) } @@ -1539,8 +1527,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidate.repr(self.tcx())); match candidate { - // FIXME -- see assemble_builtin_bound_candidates() - BuiltinCandidate(_) | + BuiltinCandidate(builtin_bound) => { + Ok(VtableBuiltin( + try!(self.confirm_builtin_candidate(obligation, builtin_bound)))) + } + ErrorCandidate => { Ok(VtableBuiltin(VtableBuiltinData { nested: VecPerParamSpace::empty() })) } @@ -1590,8 +1581,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match try!(self.builtin_bound(bound, obligation.self_ty())) { If(nested) => Ok(self.vtable_builtin_data(obligation, bound, nested)), - AmbiguousBuiltin | - ParameterBuiltin => { + AmbiguousBuiltin | ParameterBuiltin => { self.tcx().sess.span_bug( obligation.cause.span, format!("builtin bound for {} was ambig", From 594e21f19b85eb0ab32c6a18b3cf1f6d530962b2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Nov 2014 20:23:33 -0500 Subject: [PATCH 2/3] Correct various compile-fail tests. Most of the changes are because we now don't print duplicate errors within one context, so I sometimes had to break functions into two functions. --- .../compile-fail/comm-not-freeze-receiver.rs | 15 +++++++++++++ src/test/compile-fail/comm-not-freeze.rs | 4 +--- src/test/compile-fail/issue-5883.rs | 1 - src/test/compile-fail/issue-7013.rs | 2 +- src/test/compile-fail/issue-8727.rs | 4 ++-- src/test/compile-fail/kindck-copy.rs | 2 +- .../compile-fail/kindck-destructor-owned.rs | 2 +- .../compile-fail/kindck-impl-type-params.rs | 10 +++++++++ src/test/compile-fail/kindck-nonsendable-1.rs | 10 ++++++--- src/test/compile-fail/unsendable-class.rs | 2 +- src/test/compile-fail/unsized-enum.rs | 20 ++++++++++++------ src/test/compile-fail/unsized-struct.rs | 21 +++++++++++++------ src/test/compile-fail/unsized3.rs | 3 +++ src/test/compile-fail/unsized5.rs | 2 ++ 14 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 src/test/compile-fail/comm-not-freeze-receiver.rs diff --git a/src/test/compile-fail/comm-not-freeze-receiver.rs b/src/test/compile-fail/comm-not-freeze-receiver.rs new file mode 100644 index 00000000000..8cb4b6328c4 --- /dev/null +++ b/src/test/compile-fail/comm-not-freeze-receiver.rs @@ -0,0 +1,15 @@ +// Copyright 2013 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. + +fn test() {} + +fn main() { + test::>(); //~ ERROR: `core::kinds::Sync` is not implemented +} diff --git a/src/test/compile-fail/comm-not-freeze.rs b/src/test/compile-fail/comm-not-freeze.rs index b6277a3e2bd..8c17895eb8a 100644 --- a/src/test/compile-fail/comm-not-freeze.rs +++ b/src/test/compile-fail/comm-not-freeze.rs @@ -11,7 +11,5 @@ fn test() {} fn main() { - test::>(); //~ ERROR: `core::kinds::Sync` is not implemented - test::>(); //~ ERROR: `core::kinds::Sync` is not implemented - test::>(); //~ ERROR: `core::kinds::Sync` is not implemented + test::>(); //~ ERROR: `core::kinds::Sync` is not implemented } diff --git a/src/test/compile-fail/issue-5883.rs b/src/test/compile-fail/issue-5883.rs index 96088052e91..e6ac30139c3 100644 --- a/src/test/compile-fail/issue-5883.rs +++ b/src/test/compile-fail/issue-5883.rs @@ -18,7 +18,6 @@ fn new_struct(r: A+'static) -> Struct { //~^ ERROR the trait `core::kinds::Sized` is not implemented //~^ ERROR the trait `core::kinds::Sized` is not implemented Struct { r: r } - //~^ ERROR the trait `core::kinds::Sized` is not implemented } trait Curve {} diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index 3d16ff0a3fa..bef8ca6b86c 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -32,5 +32,5 @@ struct A { fn main() { let a = A {v: box B{v: None} as Box}; - //~^ ERROR the trait `core::kinds::Send` is not implemented for the type `B` + //~^ ERROR the trait `core::kinds::Send` is not implemented } diff --git a/src/test/compile-fail/issue-8727.rs b/src/test/compile-fail/issue-8727.rs index e2790eb7b39..d1a86d334cb 100644 --- a/src/test/compile-fail/issue-8727.rs +++ b/src/test/compile-fail/issue-8727.rs @@ -8,14 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// error-pattern:reached the recursion limit during monomorphization + // Verify the compiler fails with an error on infinite function // recursions. - struct Data(Box>); fn generic( _ : Vec<(Data,T)> ) { - //~^ ERROR reached the recursion limit during monomorphization let rec : Vec<(Data,(bool,T))> = Vec::new(); generic( rec ); } diff --git a/src/test/compile-fail/kindck-copy.rs b/src/test/compile-fail/kindck-copy.rs index 202529c30b3..f0c4a4243ac 100644 --- a/src/test/compile-fail/kindck-copy.rs +++ b/src/test/compile-fail/kindck-copy.rs @@ -22,7 +22,7 @@ struct MyStruct { } struct MyNoncopyStruct { - x: Box, + x: Box, } fn test<'a,T,U:Copy>(_: &'a int) { diff --git a/src/test/compile-fail/kindck-destructor-owned.rs b/src/test/compile-fail/kindck-destructor-owned.rs index 26b0c5503e3..1a82a2b3832 100644 --- a/src/test/compile-fail/kindck-destructor-owned.rs +++ b/src/test/compile-fail/kindck-destructor-owned.rs @@ -16,7 +16,7 @@ struct Foo { } impl Drop for Foo { -//~^ ERROR the trait `core::kinds::Send` is not implemented for the type `Foo` +//~^ ERROR the trait `core::kinds::Send` is not implemented //~^^ NOTE cannot implement a destructor on a structure or enumeration that does not satisfy Send fn drop(&mut self) { } diff --git a/src/test/compile-fail/kindck-impl-type-params.rs b/src/test/compile-fail/kindck-impl-type-params.rs index c92887965c0..a430fe72333 100644 --- a/src/test/compile-fail/kindck-impl-type-params.rs +++ b/src/test/compile-fail/kindck-impl-type-params.rs @@ -22,6 +22,10 @@ fn f(val: T) { let a = &t as &Gettable; //~^ ERROR the trait `core::kinds::Send` is not implemented //~^^ ERROR the trait `core::kinds::Copy` is not implemented +} + +fn g(val: T) { + let t: S = S; let a: &Gettable = &t; //~^ ERROR the trait `core::kinds::Send` is not implemented //~^^ ERROR the trait `core::kinds::Copy` is not implemented @@ -30,9 +34,15 @@ fn f(val: T) { fn foo<'a>() { let t: S<&'a int> = S; let a = &t as &Gettable<&'a int>; +} + +fn foo2<'a>() { let t: Box> = box S; let a = t as Box>; //~^ ERROR the trait `core::kinds::Copy` is not implemented +} + +fn foo3<'a>() { let t: Box> = box S; let a: Box> = t; //~^ ERROR the trait `core::kinds::Copy` is not implemented diff --git a/src/test/compile-fail/kindck-nonsendable-1.rs b/src/test/compile-fail/kindck-nonsendable-1.rs index 6ca3f0174bb..d694fd2c795 100644 --- a/src/test/compile-fail/kindck-nonsendable-1.rs +++ b/src/test/compile-fail/kindck-nonsendable-1.rs @@ -13,10 +13,14 @@ use std::rc::Rc; fn foo(_x: Rc) {} -fn main() { +fn bar() { let x = Rc::new(3u); let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented - let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented - let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented +} + +fn bar2() { + let x = Rc::new(3u); let _: proc() = proc() foo(x); } + +fn main() { } diff --git a/src/test/compile-fail/unsendable-class.rs b/src/test/compile-fail/unsendable-class.rs index 102cb636550..d988a245700 100644 --- a/src/test/compile-fail/unsendable-class.rs +++ b/src/test/compile-fail/unsendable-class.rs @@ -29,5 +29,5 @@ fn foo(i:int, j: Rc) -> foo { fn main() { let cat = "kitty".to_string(); let (tx, _) = channel(); //~ ERROR `core::kinds::Send` is not implemented - tx.send(foo(42, Rc::new(cat))); //~ ERROR `core::kinds::Send` is not implemented + tx.send(foo(42, Rc::new(cat))); } diff --git a/src/test/compile-fail/unsized-enum.rs b/src/test/compile-fail/unsized-enum.rs index edef3ae6492..0462a2025d2 100644 --- a/src/test/compile-fail/unsized-enum.rs +++ b/src/test/compile-fail/unsized-enum.rs @@ -8,14 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum Foo { FooSome(T), FooNone } -fn bar() { } -fn foo() { bar::>() } +fn is_sized() { } +fn not_sized() { } + +enum Foo { FooSome(U), FooNone } +fn foo1() { not_sized::>() } // Hunky dory. +fn foo2() { not_sized::>() } //~^ ERROR the trait `core::kinds::Sized` is not implemented -//~^^ ERROR the trait `core::kinds::Sized` is not implemented // -// One error is for T being provided to Foo, the other is -// for Foo being provided to bar. +// Not OK: `T` is not sized. + +enum Bar { BarSome(U), BarNone } +fn bar1() { not_sized::>() } +fn bar2() { is_sized::>() } +//~^ ERROR the trait `core::kinds::Sized` is not implemented +// +// Not OK: `Bar` is not sized, but it should be. fn main() { } diff --git a/src/test/compile-fail/unsized-struct.rs b/src/test/compile-fail/unsized-struct.rs index 58aba1a2646..db2e9cb9328 100644 --- a/src/test/compile-fail/unsized-struct.rs +++ b/src/test/compile-fail/unsized-struct.rs @@ -8,13 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Foo { data: T } -fn bar() { } -fn foo() { bar::>() } +fn is_sized() { } +fn not_sized() { } + +struct Foo { data: T } +fn foo1() { not_sized::>() } // Hunky dory. +fn foo2() { not_sized::>() } //~^ ERROR the trait `core::kinds::Sized` is not implemented -//~^^ ERROR the trait `core::kinds::Sized` is not implemented -// One error is for the T in Foo, the other is for Foo as a value -// for bar's type parameter. +// +// Not OK: `T` is not sized. + +struct Bar { data: T } +fn bar1() { not_sized::>() } +fn bar2() { is_sized::>() } +//~^ ERROR the trait `core::kinds::Sized` is not implemented +// +// Not OK: `Bar` is not sized, but it should be. fn main() { } diff --git a/src/test/compile-fail/unsized3.rs b/src/test/compile-fail/unsized3.rs index fba1237340f..0a75240f2d8 100644 --- a/src/test/compile-fail/unsized3.rs +++ b/src/test/compile-fail/unsized3.rs @@ -57,6 +57,9 @@ fn f8(x1: &S, x2: &S) { fn f9(x1: Box>, x2: Box>) { f5(&(*x1, 34i)); //~^ ERROR the trait `core::kinds::Sized` is not implemented +} + +fn f10(x1: Box>, x2: Box>) { f5(&(32i, *x2)); //~^ ERROR the trait `core::kinds::Sized` is not implemented } diff --git a/src/test/compile-fail/unsized5.rs b/src/test/compile-fail/unsized5.rs index 2f1eb35a426..d9d7a86889f 100644 --- a/src/test/compile-fail/unsized5.rs +++ b/src/test/compile-fail/unsized5.rs @@ -29,6 +29,8 @@ struct S4 { } enum E { V1(X, int), //~ERROR `core::kinds::Sized` is not implemented +} +enum F { V2{f1: X, f: int}, //~ERROR `core::kinds::Sized` is not implemented } From 931758c88a3d6deb95d55ef30fba71144815009e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Nov 2014 07:54:24 -0500 Subject: [PATCH 3/3] FIXME(#19481) -- workaround valgrind cleanup failure (but the code is nicer this way anyhow) --- src/librustc_trans/trans/base.rs | 2 +- src/librustc_trans/trans/callee.rs | 2 +- src/librustc_trans/trans/glue.rs | 2 +- src/test/compile-fail/dst-object-from-unsized-type.rs | 10 ++++++++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 71439f5887b..23072dee3b6 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -965,7 +965,7 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, llfn: ValueRef, - llargs: Vec , + llargs: &[ValueRef], fn_ty: Ty<'tcx>, call_info: Option, // FIXME(15064) is_lang_item is a horrible hack, please remove it diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 80a17465d78..50d2f885afa 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -808,7 +808,7 @@ pub fn trans_call_inner<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Invoke the actual rust fn and update bcx/llresult. let (llret, b) = base::invoke(bcx, llfn, - llargs, + llargs[], callee_ty, call_info, dest.is_none()); diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 4ed79837896..fbaf1e65810 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -291,7 +291,7 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let dtor_ty = ty::mk_ctor_fn(bcx.tcx(), &[get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil(bcx.tcx())); - let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None, false); + let (_, variant_cx) = invoke(variant_cx, dtor_addr, args[], dtor_ty, None, false); variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope); variant_cx diff --git a/src/test/compile-fail/dst-object-from-unsized-type.rs b/src/test/compile-fail/dst-object-from-unsized-type.rs index e40cc342c0b..99c63c3c6e9 100644 --- a/src/test/compile-fail/dst-object-from-unsized-type.rs +++ b/src/test/compile-fail/dst-object-from-unsized-type.rs @@ -13,18 +13,24 @@ trait Foo for Sized? {} impl Foo for str {} -fn test(t: &T) { +fn test1(t: &T) { let u: &Foo = t; //~^ ERROR `core::kinds::Sized` is not implemented for the type `T` +} +fn test2(t: &T) { let v: &Foo = t as &Foo; //~^ ERROR `core::kinds::Sized` is not implemented for the type `T` } -fn main() { +fn test3() { let _: &[&Foo] = &["hi"]; //~^ ERROR `core::kinds::Sized` is not implemented for the type `str` +} +fn test4() { let _: &Foo = "hi" as &Foo; //~^ ERROR `core::kinds::Sized` is not implemented for the type `str` } + +fn main() { }