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 bf4273dbfd081..25c86be993f70 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 9ce6947731dc1..71a183b475c6f 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 | 19 +++++++++++++----- src/test/compile-fail/unsized3.rs | 3 +++ src/test/compile-fail/unsized5.rs | 2 ++ 14 files changed, 72 insertions(+), 24 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 0000000000000..8cb4b6328c490 --- /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 b6277a3e2bd98..8c17895eb8a02 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 96088052e91bf..e6ac30139c37f 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 3d16ff0a3fac9..bef8ca6b86cba 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 e2790eb7b39d9..d1a86d334cb44 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 202529c30b3e6..f0c4a4243acc9 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 26b0c5503e32f..1a82a2b3832cb 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 c92887965c0d2..a430fe72333cd 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 6ca3f0174bb74..d694fd2c79512 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 102cb636550db..d988a245700f2 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 edef3ae649269..0462a2025d2ce 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 +// +// 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 -//~^^ 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: `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 58aba1a264648..db2e9cb932800 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. + +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 +// +// Not OK: `T` is not sized. -fn bar() { } -fn foo() { bar::>() } +struct Bar { data: T } +fn bar1() { not_sized::>() } +fn bar2() { is_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: `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 fba1237340fe5..0a75240f2d89d 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 2f1eb35a42623..d9d7a86889f57 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 71439f5887b1a..23072dee3b6b1 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 80a17465d7899..50d2f885afa17 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 4ed7983789696..fbaf1e6581060 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 e40cc342c0b49..99c63c3c6e95e 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() { }