From 7d21f21f71b9b4a8a0662a223c20db7a789f5637 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 29 Jul 2017 04:47:12 +0300 Subject: [PATCH 01/10] syntax: Relax path grammar --- src/libsyntax/parse/parser.rs | 15 ++------------- src/test/compile-fail/issue-32995.rs | 8 ++------ src/test/compile-fail/issue-36116.rs | 15 +++++++++------ .../unboxed-closure-sugar-used-on-struct-3.rs | 12 +++++------- .../parse-fail/type-parameters-in-field-exprs.rs | 2 ++ 5 files changed, 20 insertions(+), 32 deletions(-) rename src/test/{parse-fail => compile-fail}/unboxed-closure-sugar-used-on-struct-3.rs (68%) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 7bf4c6799b3..9e36adf3d35 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -84,7 +84,7 @@ pub enum PathStyle { Expr, /// In other contexts, notably in types, no ambiguity exists and paths can be written /// without the disambiguator, e.g. `x` - unambiguously a path. - /// Paths with disambiguators are rejected for now, but may be allowed in the future. + /// Paths with disambiguators are still accepted, `x::` - unambiguously a path too. Type, /// A path with generic arguments disallowed, e.g. `foo::bar::Baz`, used in imports, /// visibilities or attributes. @@ -1835,18 +1835,7 @@ impl<'a> Parser<'a> { && self.look_ahead(1, |t| is_args_start(t)) { // Generic arguments are found - `<`, `(`, `::<` or `::(`. let lo = self.span; - if self.eat(&token::ModSep) { - // These errors are not strictly necessary and may be removed in the future. - if style == PathStyle::Type { - let mut err = self.diagnostic().struct_span_err(self.prev_span, - "unnecessary path disambiguator"); - err.span_label(self.prev_span, "try removing `::`"); - err.emit(); - } else if self.token == token::OpenDelim(token::Paren) { - self.diagnostic().span_err(self.prev_span, - "`::` is not supported before parenthesized generic arguments") - } - } + self.eat(&token::ModSep); let parameters = if self.eat_lt() { // `<'a, T, A = U>` diff --git a/src/test/compile-fail/issue-32995.rs b/src/test/compile-fail/issue-32995.rs index 4b7f82943ba..ffbd0c0c22a 100644 --- a/src/test/compile-fail/issue-32995.rs +++ b/src/test/compile-fail/issue-32995.rs @@ -19,15 +19,11 @@ fn main() { //~^ ERROR parenthesized parameters may only be used with a trait //~| WARN previously accepted - macro_rules! pathexpr { - ($p:path) => { $p } - } - - let p = pathexpr!(::std::str()::from_utf8)(b"foo").unwrap(); + let p = ::std::str::()::from_utf8(b"foo").unwrap(); //~^ ERROR parenthesized parameters may only be used with a trait //~| WARN previously accepted - let p = pathexpr!(::std::str::from_utf8())(b"foo").unwrap(); + let p = ::std::str::from_utf8::()(b"foo").unwrap(); //~^ ERROR parenthesized parameters may only be used with a trait //~| WARN previously accepted diff --git a/src/test/compile-fail/issue-36116.rs b/src/test/compile-fail/issue-36116.rs index 737955b2ff3..18a6e430b84 100644 --- a/src/test/compile-fail/issue-36116.rs +++ b/src/test/compile-fail/issue-36116.rs @@ -8,16 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Unnecessary path disambiguator is ok + +#![feature(rustc_attrs)] +#![allow(unused)] + struct Foo { _a: T, } -fn main() { +fn f() { let f = Some(Foo { _a: 42 }).map(|a| a as Foo::); - //~^ ERROR unnecessary path disambiguator - //~| NOTE try removing `::` - let g: Foo:: = Foo { _a: 42 }; - //~^ ERROR unnecessary path disambiguator - //~| NOTE try removing `::` } + +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs b/src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs similarity index 68% rename from src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs rename to src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs index 548a5078a74..42fffe546c2 100644 --- a/src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs @@ -8,9 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only - -// Test that parentheses form doesn't work in expression paths. +// Test that parentheses form parses in expression paths. struct Bar { f: A, r: R @@ -21,10 +19,10 @@ impl Bar { } fn bar() { - let b = Box::Bar::::new(); // OK + let b = Bar::::new(); // OK - let b = Box::Bar::()::new(); - //~^ ERROR `::` is not supported before parenthesized generic arguments + let b = Bar::(isize, usize)::new(); // OK too (for the parser) + //~^ ERROR parenthesized parameters may only be used with a trait } -fn main() { } +fn main() {} diff --git a/src/test/parse-fail/type-parameters-in-field-exprs.rs b/src/test/parse-fail/type-parameters-in-field-exprs.rs index 95c307c5670..cb018ff1bfa 100644 --- a/src/test/parse-fail/type-parameters-in-field-exprs.rs +++ b/src/test/parse-fail/type-parameters-in-field-exprs.rs @@ -24,4 +24,6 @@ fn main() { //~^ ERROR field expressions may not have generic arguments f.x::<>; //~^ ERROR field expressions may not have generic arguments + f.x::(); + //~^ ERROR field expressions may not have generic arguments } From 804459bdca28010137990220e617a6b6cbab18d0 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 11 Aug 2017 02:30:08 +0300 Subject: [PATCH 02/10] Issue warnings for unnecessary path disambiguators --- src/libsyntax/ext/tt/macro_parser.rs | 4 +--- src/libsyntax/parse/parser.rs | 28 ++++++++++++++++++---------- src/test/compile-fail/issue-36116.rs | 15 +++++++++++++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 146bd5d9856..16a6a0717e0 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -599,9 +599,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { panic!(FatalError) } }, - "path" => { - token::NtPath(panictry!(p.parse_path(PathStyle::Type))) - }, + "path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))), "meta" => token::NtMeta(panictry!(p.parse_meta_item())), "vis" => token::NtVis(panictry!(p.parse_visibility(true))), // this is not supposed to happen, since it has been checked diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9e36adf3d35..fbf7c4adf37 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1755,7 +1755,7 @@ impl<'a> Parser<'a> { self.expect(&token::ModSep)?; let qself = QSelf { ty, position: path.segments.len() }; - self.parse_path_segments(&mut path.segments, style)?; + self.parse_path_segments(&mut path.segments, style, true)?; Ok((qself, ast::Path { segments: path.segments, span: lo.to(self.prev_span) })) } @@ -1770,8 +1770,12 @@ impl<'a> Parser<'a> { /// `a::b::C::` (with disambiguator) /// `Fn(Args)` (without disambiguator) /// `Fn::(Args)` (with disambiguator) - pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path> - { + pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path> { + self.parse_path_common(style, true) + } + + pub fn parse_path_common(&mut self, style: PathStyle, enable_warning: bool) + -> PResult<'a, ast::Path> { maybe_whole!(self, NtPath, |x| x); let lo = self.meta_var_span.unwrap_or(self.span); @@ -1779,7 +1783,7 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { segments.push(PathSegment::crate_root(lo)); } - self.parse_path_segments(&mut segments, style)?; + self.parse_path_segments(&mut segments, style, enable_warning)?; Ok(ast::Path { segments, span: lo.to(self.prev_span) }) } @@ -1804,10 +1808,10 @@ impl<'a> Parser<'a> { self.parse_path(style) } - fn parse_path_segments(&mut self, segments: &mut Vec, style: PathStyle) - -> PResult<'a, ()> { + fn parse_path_segments(&mut self, segments: &mut Vec, style: PathStyle, + enable_warning: bool) -> PResult<'a, ()> { loop { - segments.push(self.parse_path_segment(style)?); + segments.push(self.parse_path_segment(style, enable_warning)?); if self.is_import_coupler() || !self.eat(&token::ModSep) { return Ok(()); @@ -1815,7 +1819,8 @@ impl<'a> Parser<'a> { } } - fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { + fn parse_path_segment(&mut self, style: PathStyle, enable_warning: bool) + -> PResult<'a, PathSegment> { let ident_span = self.span; let ident = self.parse_path_segment_ident()?; @@ -1835,7 +1840,10 @@ impl<'a> Parser<'a> { && self.look_ahead(1, |t| is_args_start(t)) { // Generic arguments are found - `<`, `(`, `::<` or `::(`. let lo = self.span; - self.eat(&token::ModSep); + if self.eat(&token::ModSep) && style == PathStyle::Type && enable_warning { + self.diagnostic().struct_span_warn(self.prev_span, "unnecessary path disambiguator") + .span_label(self.prev_span, "try removing `::`").emit(); + } let parameters = if self.eat_lt() { // `<'a, T, A = U>` @@ -2371,7 +2379,7 @@ impl<'a> Parser<'a> { // Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { - let segment = self.parse_path_segment(PathStyle::Expr)?; + let segment = self.parse_path_segment(PathStyle::Expr, true)?; Ok(match self.token { token::OpenDelim(token::Paren) => { // Method call `expr.f()` diff --git a/src/test/compile-fail/issue-36116.rs b/src/test/compile-fail/issue-36116.rs index 18a6e430b84..3afbfa61984 100644 --- a/src/test/compile-fail/issue-36116.rs +++ b/src/test/compile-fail/issue-36116.rs @@ -13,13 +13,24 @@ #![feature(rustc_attrs)] #![allow(unused)] +macro_rules! m { + ($p: path) => { + let _ = $p(0); + let _: $p; + } +} + struct Foo { _a: T, } +struct S(T); + fn f() { - let f = Some(Foo { _a: 42 }).map(|a| a as Foo::); - let g: Foo:: = Foo { _a: 42 }; + let f = Some(Foo { _a: 42 }).map(|a| a as Foo::); //~ WARN unnecessary path disambiguator + let g: Foo:: = Foo { _a: 42 }; //~ WARN unnecessary path disambiguator + + m!(S::); // OK, no warning } #[rustc_error] From 0c3ac648f85cca1e8dd89dfff727a422bc1897a6 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 4 Aug 2017 14:44:12 +0200 Subject: [PATCH 03/10] Make `Clone` a lang item and generate builtin impls. Fixes #28229. Fixes #24000. --- src/libcore/array.rs | 1 + src/libcore/clone.rs | 3 + src/libcore/ptr.rs | 3 + src/libcore/tuple.rs | 1 + src/librustc/ich/impls_ty.rs | 4 + src/librustc/middle/lang_items.rs | 4 + src/librustc/traits/mod.rs | 7 +- src/librustc/traits/select.rs | 27 +++- src/librustc/traits/structural_impls.rs | 16 +- src/librustc/ty/instance.rs | 26 +++- src/librustc/ty/mod.rs | 3 +- src/librustc_mir/shim.rs | 179 +++++++++++++++++++++- src/librustc_trans/collector.rs | 6 +- src/librustc_trans/monomorphize.rs | 6 + src/librustc_trans/partitioning.rs | 6 +- src/librustc_typeck/check/method/probe.rs | 45 ++++++ src/test/run-pass/issue-37725.rs | 8 +- 17 files changed, 313 insertions(+), 32 deletions(-) diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 37bd57034a7..0d9f9d16d82 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -124,6 +124,7 @@ macro_rules! array_impls { } #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(stage0)] impl Clone for [T; $N] { fn clone(&self) -> [T; $N] { *self diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index 8856d0b8cb9..2dc51718b97 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -88,6 +88,7 @@ /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(stage0), lang = "clone")] pub trait Clone : Sized { /// Returns a copy of the value. /// @@ -131,6 +132,7 @@ pub struct AssertParamIsClone { _field: ::marker::PhantomData pub struct AssertParamIsCopy { _field: ::marker::PhantomData } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(stage0)] impl<'a, T: ?Sized> Clone for &'a T { /// Returns a shallow copy of the reference. #[inline] @@ -140,6 +142,7 @@ impl<'a, T: ?Sized> Clone for &'a T { macro_rules! clone_impl { ($t:ty) => { #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(stage0)] impl Clone for $t { /// Returns a deep copy of the value. #[inline] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index e35777d222c..63e9373e936 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -876,6 +876,7 @@ pub fn eq(a: *const T, b: *const T) -> bool { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(stage0)] impl Clone for *const T { #[inline] fn clone(&self) -> *const T { @@ -884,6 +885,7 @@ impl Clone for *const T { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(stage0)] impl Clone for *mut T { #[inline] fn clone(&self) -> *mut T { @@ -895,6 +897,7 @@ impl Clone for *mut T { macro_rules! fnptr_impls_safety_abi { ($FnTy: ty, $($Arg: ident),*) => { #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(stage0)] impl Clone for $FnTy { #[inline] fn clone(&self) -> Self { diff --git a/src/libcore/tuple.rs b/src/libcore/tuple.rs index 47e9c7c9038..555843dba41 100644 --- a/src/libcore/tuple.rs +++ b/src/libcore/tuple.rs @@ -22,6 +22,7 @@ macro_rules! tuple_impls { )+) => { $( #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(stage0)] impl<$($T:Clone),+> Clone for ($($T,)+) { fn clone(&self) -> ($($T,)+) { ($(self.$idx.clone(),)+) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 8a37d7bab44..5b75ce7864f 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -721,6 +721,10 @@ impl<'a, 'gcx, 'tcx> HashStable> for ty::In def_id.hash_stable(hcx, hasher); t.hash_stable(hcx, hasher); } + ty::InstanceDef::BuiltinShim(def_id, t) => { + def_id.hash_stable(hcx, hasher); + t.hash_stable(hcx, hasher); + } } } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 01ed79096b1..8cee88bd39b 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -274,6 +274,7 @@ language_item_table! { SizedTraitLangItem, "sized", sized_trait; UnsizeTraitLangItem, "unsize", unsize_trait; CopyTraitLangItem, "copy", copy_trait; + CloneTraitLangItem, "clone", clone_trait; SyncTraitLangItem, "sync", sync_trait; FreezeTraitLangItem, "freeze", freeze_trait; @@ -320,6 +321,9 @@ language_item_table! { StrEqFnLangItem, "str_eq", str_eq_fn; + CloneMethodLangItem, "clone_method", clone_method; + CloneFromMethodLangItem, "clone_from_method", clone_from_method; + // A number of panic-related lang items. The `panic` item corresponds to // divide-by-zero and various panic cases with `match`. The // `panic_bounds_check` item is for indexing arrays. diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index d1938197e65..1c6d75ace52 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -301,7 +301,7 @@ pub enum Vtable<'tcx, N> { VtableObject(VtableObjectData<'tcx, N>), /// Successful resolution for a builtin trait. - VtableBuiltin(VtableBuiltinData), + VtableBuiltin(VtableBuiltinData<'tcx, N>), /// Vtable automatically generated for a closure. The def ID is the ID /// of the closure expression. This is a `VtableImpl` in spirit, but the @@ -345,7 +345,9 @@ pub struct VtableDefaultImplData { } #[derive(Clone)] -pub struct VtableBuiltinData { +pub struct VtableBuiltinData<'tcx, N> { + /// `ty` can be used for generating shim for builtin implementations like `Clone::clone`. + pub ty: ty::Ty<'tcx>, pub nested: Vec } @@ -769,6 +771,7 @@ impl<'tcx, N> Vtable<'tcx, N> { }), VtableParam(n) => VtableParam(n.into_iter().map(f).collect()), VtableBuiltin(i) => VtableBuiltin(VtableBuiltinData { + ty: i.ty, nested: i.nested.into_iter().map(f).collect(), }), VtableObject(o) => VtableObject(VtableObjectData { diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 3e39d592135..88cca70993e 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1296,6 +1296,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } else if self.tcx().lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else { + if self.tcx().lang_items.clone_trait() == Some(def_id) { + // Same builtin conditions as `Copy`, i.e. every type which has builtin support + // for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone` + // types have builtin support for `Clone`. + let clone_conditions = self.copy_conditions(obligation); + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + } + self.assemble_closure_candidates(obligation, &mut candidates)?; self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; self.assemble_candidates_from_impls(obligation, &mut candidates)?; @@ -2164,8 +2172,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { match candidate { BuiltinCandidate { has_nested } => { - Ok(VtableBuiltin( - self.confirm_builtin_candidate(obligation, has_nested))) + let data = self.confirm_builtin_candidate(obligation, has_nested); + Ok(VtableBuiltin(data)) } ParamCandidate(param) => { @@ -2257,7 +2265,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { fn confirm_builtin_candidate(&mut self, obligation: &TraitObligation<'tcx>, has_nested: bool) - -> VtableBuiltinData> + -> VtableBuiltinData<'tcx, PredicateObligation<'tcx>> { debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); @@ -2271,6 +2279,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { _ if Some(trait_def) == self.tcx().lang_items.copy_trait() => { self.copy_conditions(obligation) } + _ if Some(trait_def) == self.tcx().lang_items.clone_trait() => { + self.copy_conditions(obligation) + } _ => bug!("unexpected builtin trait {:?}", trait_def) }; let nested = match conditions { @@ -2291,7 +2302,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { debug!("confirm_builtin_candidate: obligations={:?}", obligations); - VtableBuiltinData { nested: obligations } + + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + VtableBuiltinData { ty: self_ty, nested: obligations } } /// This handles the case where a `impl Foo for ..` impl is being used. @@ -2598,8 +2611,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { fn confirm_builtin_unsize_candidate(&mut self, obligation: &TraitObligation<'tcx>,) - -> Result>, - SelectionError<'tcx>> { + -> Result>, SelectionError<'tcx>> + { let tcx = self.tcx(); // assemble_candidates_for_unsizing should ensure there are no late bound @@ -2801,7 +2814,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { _ => bug!() }; - Ok(VtableBuiltinData { nested: nested }) + Ok(VtableBuiltinData { ty: source, nested: nested }) } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index f1c176561ea..a83849898f5 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -86,9 +86,9 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableClosureData<'tcx, N> { } } -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableBuiltinData { +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableBuiltinData<'tcx, N> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VtableBuiltin(nested={:?})", self.nested) + write!(f, "VtableBuiltin(ty={:?}, nested={:?})", self.ty, self.nested) } } @@ -300,7 +300,14 @@ impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { }) } traits::VtableParam(n) => Some(traits::VtableParam(n)), - traits::VtableBuiltin(d) => Some(traits::VtableBuiltin(d)), + traits::VtableBuiltin(traits::VtableBuiltinData { ty, nested }) => { + tcx.lift(&ty).map(|ty| { + traits::VtableBuiltin(traits::VtableBuiltinData { + ty, + nested, + }) + }) + } traits::VtableObject(traits::VtableObjectData { upcast_trait_ref, vtable_base, @@ -378,9 +385,10 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultIm } } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData { +impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData<'tcx, N> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { traits::VtableBuiltinData { + ty: self.ty.fold_with(folder), nested: self.nested.fold_with(folder), } } diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 32063a2dda6..e8daf7c09f4 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -24,15 +24,22 @@ pub struct Instance<'tcx> { pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), - // ::call_* - // def-id is FnTrait::call_* + + /// ::call_* + /// def-id is FnTrait::call_* FnPtrShim(DefId, Ty<'tcx>), - // ::fn + + /// ::fn Virtual(DefId, usize), - // <[mut closure] as FnOnce>::call_once + + /// <[mut closure] as FnOnce>::call_once ClosureOnceShim { call_once: DefId }, - // drop_in_place::; None for empty drop glue. + + /// drop_in_place::; None for empty drop glue. DropGlue(DefId, Option>), + + /// Builtin method implementation, e.g. `Clone::clone`. + BuiltinShim(DefId, Ty<'tcx>), } impl<'tcx> InstanceDef<'tcx> { @@ -43,9 +50,9 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id, ) | - InstanceDef::ClosureOnceShim { call_once: def_id } - => def_id, - InstanceDef::DropGlue(def_id, _) => def_id + InstanceDef::ClosureOnceShim { call_once: def_id } | + InstanceDef::DropGlue(def_id, _) | + InstanceDef::BuiltinShim(def_id, _) => def_id } } @@ -80,6 +87,9 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::DropGlue(_, ty) => { write!(f, " - shim({:?})", ty) } + InstanceDef::BuiltinShim(_, ty) => { + write!(f, " - shim({:?})", ty) + } } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 28a73f4a4d3..81a0af63942 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2227,7 +2227,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } | - ty::InstanceDef::DropGlue(..) => { + ty::InstanceDef::DropGlue(..) | + ty::InstanceDef::BuiltinShim(..) => { self.mir_shims(instance) } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 62e762be93a..cb1a2f6b107 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -17,6 +17,7 @@ use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; +use rustc_const_math::{ConstInt, ConstUsize}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -98,14 +99,26 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, ty::InstanceDef::DropGlue(def_id, ty) => { build_drop_shim(tcx, def_id, ty) } + ty::InstanceDef::BuiltinShim(def_id, ty) => { + let name = tcx.item_name(def_id).as_str(); + let trait_id = tcx.trait_of_item(def_id); + if trait_id == tcx.lang_items.clone_trait() && name == "clone" { + build_clone_shim(tcx, def_id, ty) + } else if trait_id == tcx.lang_items.clone_trait() && name == "clone_from" { + debug!("make_shim({:?}: using default trait implementation", instance); + return tcx.optimized_mir(def_id); + } else { + bug!("builtin shim {:?} not supported", instance) + } + } ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } }; - debug!("make_shim({:?}) = untransformed {:?}", instance, result); - no_landing_pads::no_landing_pads(tcx, &mut result); - simplify::simplify_cfg(&mut result); - add_call_guards::CriticalCallEdges.add_call_guards(&mut result); + debug!("make_shim({:?}) = untransformed {:?}", instance, result); + no_landing_pads::no_landing_pads(tcx, &mut result); + simplify::simplify_cfg(&mut result); + add_call_guards::CriticalCallEdges.add_call_guards(&mut result); debug!("make_shim({:?}) = {:?}", instance, result); tcx.alloc_mir(result) @@ -259,6 +272,164 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } +/// Build a `Clone::clone` shim for `recvr_ty`. Here, `def_id` is `Clone::clone`. +fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + recvr_ty: ty::Ty<'tcx>) + -> Mir<'tcx> +{ + let sig = tcx.fn_sig(def_id); + let sig = tcx.erase_late_bound_regions(&sig); + let span = tcx.def_span(def_id); + + debug!("build_clone_shim(def_id={:?})", def_id); + + let mut local_decls = local_decls_for_sig(&sig, span); + let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + + let mut blocks = IndexVec::new(); + let block = |blocks: &mut IndexVec<_, _>, statement, kind| { + blocks.push(BasicBlockData { + statements: vec![statement], + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false + }) + }; + + let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + + let call_clone = |i, ty, rcvr_field, blocks: &mut _, local_decls: &mut IndexVec<_, _>| { + // `func == Clone::clone(&ty) -> ty` + let substs = Substs::for_item(tcx, def_id, |_, _| tcx.types.re_erased, |_, _| ty); + let func = Operand::Constant(box Constant { + span: span, + ty: tcx.mk_fn_def(def_id, substs), + literal: Literal::Value { + value: ConstVal::Function(def_id, substs), + }, + }); + + let ref_loc = Lvalue::Local( + local_decls.push(temp_decl( + Mutability::Not, + tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { + ty, + mutbl: hir::Mutability::MutImmutable, + }), + span + )) + ); + + let loc = Lvalue::Local( + local_decls.push(temp_decl( + Mutability::Not, + ty, + span + )) + ); + + // `let ref_loc: &ty = &rcvr_field;` + let statement = Statement { + source_info: source_info, + kind: StatementKind::Assign( + ref_loc.clone(), + Rvalue::Ref(tcx.types.re_erased, BorrowKind::Shared, rcvr_field) + ) + }; + + // `let loc = Clone::clone(ref_loc);` + block(blocks, statement, TerminatorKind::Call { + func, + args: vec![Operand::Consume(ref_loc)], + destination: Some((loc.clone(), BasicBlock::new(i + 1))), + cleanup: None, + }); + + loc + }; + + match recvr_ty.sty { + ty::TyArray(ty, len) => { + let mut returns = Vec::new(); + for i in 0..len { + let index = ConstUsize::new(i as u64, tcx.sess.target.uint_type).unwrap(); + let rcvr_field = rcvr.clone().index( + Operand::Constant(box Constant { + span, + ty: tcx.types.usize, + literal: Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(index)) + } + }) + ); + + // `returns[i] = Clone::clone(&rcvr[i]);` + returns.push(call_clone(i, ty, rcvr_field, &mut blocks, &mut local_decls)); + } + + // `return [returns[0], returns[1], ..., returns[len - 1]];` + let statement = Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Aggregate( + box AggregateKind::Array(ty), + returns.into_iter().map(|loc| Operand::Consume(loc)).collect() + ) + ) + }; + block(&mut blocks, statement, TerminatorKind::Return); + } + ty::TyTuple(tys, _) => { + let mut returns = Vec::new(); + for (i, ity) in tys.iter().enumerate() { + let rcvr_field = rcvr.clone().field(Field::new(i), *ity); + + // `returns[i] = Clone::clone(&rcvr.i);` + returns.push(call_clone(i, *ity, rcvr_field, &mut blocks, &mut local_decls)); + } + + // `return (returns[0], returns[1], ..., returns[tys.len() - 1]);` + let statement = Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Aggregate( + box AggregateKind::Tuple, + returns.into_iter().map(|loc| Operand::Consume(loc)).collect() + ) + ) + }; + block(&mut blocks, statement, TerminatorKind::Return); + } + _ => { + // `return *self;` + let statement = Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Use(Operand::Consume(rcvr)) + ) + }; + block(&mut blocks, statement, TerminatorKind::Return); + } + }; + + let mir = Mir::new( + blocks, + IndexVec::from_elem_n( + VisibilityScopeData { span: span, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls, + sig.inputs().len(), + vec![], + span + ); + mir +} + /// Build a "call" shim for `def_id`. The shim calls the /// function specified by `call_kind`, first adjusting its first /// argument according to `rcvr_adjustment`. diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index b31295f4022..4e02810b040 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -699,7 +699,8 @@ fn visit_instance_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | - ty::InstanceDef::FnPtrShim(..) => { + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::BuiltinShim(..) => { output.push(create_fn_trans_item(instance)); } } @@ -716,7 +717,8 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan ty::InstanceDef::Virtual(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | - ty::InstanceDef::Intrinsic(_) => return true + ty::InstanceDef::Intrinsic(_) | + ty::InstanceDef::BuiltinShim(..) => return true }; match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 1f6a262162d..1936775df0a 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -143,6 +143,12 @@ fn resolve_associated_item<'a, 'tcx>( substs: rcvr_substs } } + traits::VtableBuiltin(ref data) => { + Instance { + def: ty::InstanceDef::BuiltinShim(def_id, data.ty), + substs: rcvr_substs + } + } _ => { bug!("static call to invalid vtable: {:?}", vtbl) } diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 63c7b18e8d2..0149714d0b7 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -361,7 +361,8 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>, InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) | InstanceDef::ClosureOnceShim { .. } | - InstanceDef::DropGlue(..) => { + InstanceDef::DropGlue(..) | + InstanceDef::BuiltinShim(..) => { bug!("partitioning: Encountered unexpected root translation item: {:?}", trans_item) @@ -603,7 +604,8 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::DropGlue(..) | - ty::InstanceDef::Virtual(..) => return None + ty::InstanceDef::Virtual(..) | + ty::InstanceDef::BuiltinShim(..) => return None }; // If this is a method, we want to put it into the same module as diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 7bf671d5e9f..a0985102f1c 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -719,6 +719,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { continue; } + self.assemble_builtin_candidates(import_id, trait_def_id, item.clone()); + self.assemble_extension_candidates_for_trait_impls(import_id, trait_def_id, item.clone()); @@ -732,6 +734,49 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Ok(()) } + fn assemble_builtin_candidates(&mut self, + import_id: Option, + trait_def_id: DefId, + item: ty::AssociatedItem) { + if Some(trait_def_id) == self.tcx.lang_items.clone_trait() { + self.assemble_builtin_clone_candidates(import_id, trait_def_id, item); + } + } + + fn assemble_builtin_clone_candidates(&mut self, + import_id: Option, + trait_def_id: DefId, + item: ty::AssociatedItem) { + for step in Rc::clone(&self.steps).iter() { + match step.self_ty.sty { + ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) | + ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) | + ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | + ty::TyRawPtr(..) | ty::TyError | ty::TyNever | + ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) | + ty::TyArray(..) | ty::TyTuple(..) => { + () + } + + _ => continue, + }; + + let substs = Substs::for_item(self.tcx, + trait_def_id, + |def, _| self.region_var_for_def(self.span, def), + |def, substs| { + if def.index == 0 { + step.self_ty + } else { + self.type_var_for_def(self.span, def, substs) + } + }); + + let xform_self_ty = self.xform_self_ty(&item, step.self_ty, substs); + self.push_inherent_candidate(xform_self_ty, item, TraitCandidate, import_id); + } + } + fn assemble_extension_candidates_for_trait_impls(&mut self, import_id: Option, trait_def_id: DefId, diff --git a/src/test/run-pass/issue-37725.rs b/src/test/run-pass/issue-37725.rs index 5ed1295c85c..a8fb11f9c62 100644 --- a/src/test/run-pass/issue-37725.rs +++ b/src/test/run-pass/issue-37725.rs @@ -8,7 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub fn foo<'a>(s: &'a mut ()) where &'a mut (): Clone { - s.clone(); +trait Foo { + fn foo(&self); +} + +fn foo<'a>(s: &'a mut ()) where &'a mut (): Foo { + s.foo(); } fn main() {} From f55dafee7e30befaf960017ccc997cdb0a5b0f33 Mon Sep 17 00:00:00 2001 From: scalexm Date: Sat, 5 Aug 2017 19:20:55 +0200 Subject: [PATCH 04/10] Remove dummy lang items --- src/librustc/middle/lang_items.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 8cee88bd39b..9ba4252b52e 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -321,9 +321,6 @@ language_item_table! { StrEqFnLangItem, "str_eq", str_eq_fn; - CloneMethodLangItem, "clone_method", clone_method; - CloneFromMethodLangItem, "clone_from_method", clone_from_method; - // A number of panic-related lang items. The `panic` item corresponds to // divide-by-zero and various panic cases with `match`. The // `panic_bounds_check` item is for indexing arrays. From 91e99a321569defb72cde69f2b2854b1e166219c Mon Sep 17 00:00:00 2001 From: scalexm Date: Sat, 5 Aug 2017 19:51:21 +0200 Subject: [PATCH 05/10] Add a test --- src/test/run-pass/builtin-clone.rs | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/run-pass/builtin-clone.rs diff --git a/src/test/run-pass/builtin-clone.rs b/src/test/run-pass/builtin-clone.rs new file mode 100644 index 00000000000..87af48c13b0 --- /dev/null +++ b/src/test/run-pass/builtin-clone.rs @@ -0,0 +1,54 @@ +// 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. + +// Test that `Clone` is correctly implemented for builtin types. +// Also test that cloning an array or a tuple is done right, i.e. +// each component is cloned. + +fn test_clone(arg: T) { + let _ = arg.clone(); +} + +fn foo() { } + +#[derive(Debug, PartialEq, Eq)] +struct S(i32); + +impl Clone for S { + fn clone(&self) -> Self { + S(self.0 + 1) + } +} + +fn main() { + test_clone(foo); + test_clone([1; 56]); + test_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); + + let a = [S(0), S(1), S(2)]; + let b = [S(1), S(2), S(3)]; + assert_eq!(b, a.clone()); + + let a = ( + (S(1), S(0)), + ( + (S(0), S(0), S(1)), + S(0) + ) + ); + let b = ( + (S(2), S(1)), + ( + (S(1), S(1), S(2)), + S(1) + ) + ); + assert_eq!(b, a.clone()); +} From 91aa99607f1076c8f76614ed6f15b1645e87e5a8 Mon Sep 17 00:00:00 2001 From: scalexm Date: Mon, 7 Aug 2017 13:22:48 +0200 Subject: [PATCH 06/10] Do not store `ty` --- src/librustc/traits/mod.rs | 7 ++----- src/librustc/traits/select.rs | 9 ++++----- src/librustc/traits/structural_impls.rs | 16 ++++------------ src/librustc_mir/shim.rs | 8 ++++---- src/librustc_trans/monomorphize.rs | 4 ++-- 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 1c6d75ace52..d1938197e65 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -301,7 +301,7 @@ pub enum Vtable<'tcx, N> { VtableObject(VtableObjectData<'tcx, N>), /// Successful resolution for a builtin trait. - VtableBuiltin(VtableBuiltinData<'tcx, N>), + VtableBuiltin(VtableBuiltinData), /// Vtable automatically generated for a closure. The def ID is the ID /// of the closure expression. This is a `VtableImpl` in spirit, but the @@ -345,9 +345,7 @@ pub struct VtableDefaultImplData { } #[derive(Clone)] -pub struct VtableBuiltinData<'tcx, N> { - /// `ty` can be used for generating shim for builtin implementations like `Clone::clone`. - pub ty: ty::Ty<'tcx>, +pub struct VtableBuiltinData { pub nested: Vec } @@ -771,7 +769,6 @@ impl<'tcx, N> Vtable<'tcx, N> { }), VtableParam(n) => VtableParam(n.into_iter().map(f).collect()), VtableBuiltin(i) => VtableBuiltin(VtableBuiltinData { - ty: i.ty, nested: i.nested.into_iter().map(f).collect(), }), VtableObject(o) => VtableObject(VtableObjectData { diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 88cca70993e..46bdb1344b2 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -2265,7 +2265,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { fn confirm_builtin_candidate(&mut self, obligation: &TraitObligation<'tcx>, has_nested: bool) - -> VtableBuiltinData<'tcx, PredicateObligation<'tcx>> + -> VtableBuiltinData> { debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); @@ -2303,8 +2303,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { debug!("confirm_builtin_candidate: obligations={:?}", obligations); - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - VtableBuiltinData { ty: self_ty, nested: obligations } + VtableBuiltinData { nested: obligations } } /// This handles the case where a `impl Foo for ..` impl is being used. @@ -2611,7 +2610,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { fn confirm_builtin_unsize_candidate(&mut self, obligation: &TraitObligation<'tcx>,) - -> Result>, SelectionError<'tcx>> + -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); @@ -2814,7 +2813,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { _ => bug!() }; - Ok(VtableBuiltinData { ty: source, nested: nested }) + Ok(VtableBuiltinData { nested: nested }) } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index a83849898f5..003508fbbca 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -86,9 +86,9 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableClosureData<'tcx, N> { } } -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableBuiltinData<'tcx, N> { +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableBuiltinData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VtableBuiltin(ty={:?}, nested={:?})", self.ty, self.nested) + write!(f, "VtableBuiltin(nested={:?})", self.nested) } } @@ -300,14 +300,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { }) } traits::VtableParam(n) => Some(traits::VtableParam(n)), - traits::VtableBuiltin(traits::VtableBuiltinData { ty, nested }) => { - tcx.lift(&ty).map(|ty| { - traits::VtableBuiltin(traits::VtableBuiltinData { - ty, - nested, - }) - }) - } + traits::VtableBuiltin(n) => Some(traits::VtableBuiltin(n)), traits::VtableObject(traits::VtableObjectData { upcast_trait_ref, vtable_base, @@ -385,10 +378,9 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultIm } } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData<'tcx, N> { +impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { traits::VtableBuiltinData { - ty: self.ty.fold_with(folder), nested: self.nested.fold_with(folder), } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index cb1a2f6b107..040d96b0dcc 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -275,7 +275,7 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { /// Build a `Clone::clone` shim for `recvr_ty`. Here, `def_id` is `Clone::clone`. fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - recvr_ty: ty::Ty<'tcx>) + rcvr_ty: ty::Ty<'tcx>) -> Mir<'tcx> { let sig = tcx.fn_sig(def_id); @@ -348,7 +348,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, loc }; - match recvr_ty.sty { + match rcvr_ty.sty { ty::TyArray(ty, len) => { let mut returns = Vec::new(); for i in 0..len { @@ -374,7 +374,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, Lvalue::Local(RETURN_POINTER), Rvalue::Aggregate( box AggregateKind::Array(ty), - returns.into_iter().map(|loc| Operand::Consume(loc)).collect() + returns.into_iter().map(Operand::Consume).collect() ) ) }; @@ -396,7 +396,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, Lvalue::Local(RETURN_POINTER), Rvalue::Aggregate( box AggregateKind::Tuple, - returns.into_iter().map(|loc| Operand::Consume(loc)).collect() + returns.into_iter().map(Operand::Consume).collect() ) ) }; diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 1936775df0a..401ee8cfaa8 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -143,9 +143,9 @@ fn resolve_associated_item<'a, 'tcx>( substs: rcvr_substs } } - traits::VtableBuiltin(ref data) => { + traits::VtableBuiltin(..) => { Instance { - def: ty::InstanceDef::BuiltinShim(def_id, data.ty), + def: ty::InstanceDef::BuiltinShim(def_id, trait_ref.self_ty()), substs: rcvr_substs } } From 4e4e55aa77a0ac5d2108a7cfc44da8784e961a85 Mon Sep 17 00:00:00 2001 From: scalexm Date: Mon, 7 Aug 2017 16:21:08 +0200 Subject: [PATCH 07/10] Rename `BuiltinShim` -> `CloneShim` --- src/librustc/ich/impls_ty.rs | 2 +- src/librustc/ty/instance.rs | 6 +++--- src/librustc/ty/mod.rs | 2 +- src/librustc_mir/shim.rs | 15 +++++++-------- src/librustc_trans/collector.rs | 4 ++-- src/librustc_trans/monomorphize.rs | 4 ++-- src/librustc_trans/partitioning.rs | 4 ++-- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 5b75ce7864f..ec957b427be 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -721,7 +721,7 @@ impl<'a, 'gcx, 'tcx> HashStable> for ty::In def_id.hash_stable(hcx, hasher); t.hash_stable(hcx, hasher); } - ty::InstanceDef::BuiltinShim(def_id, t) => { + ty::InstanceDef::CloneShim(def_id, t) => { def_id.hash_stable(hcx, hasher); t.hash_stable(hcx, hasher); } diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index e8daf7c09f4..7d543f689c2 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -39,7 +39,7 @@ pub enum InstanceDef<'tcx> { DropGlue(DefId, Option>), /// Builtin method implementation, e.g. `Clone::clone`. - BuiltinShim(DefId, Ty<'tcx>), + CloneShim(DefId, Ty<'tcx>), } impl<'tcx> InstanceDef<'tcx> { @@ -52,7 +52,7 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::Intrinsic(def_id, ) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) | - InstanceDef::BuiltinShim(def_id, _) => def_id + InstanceDef::CloneShim(def_id, _) => def_id } } @@ -87,7 +87,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::DropGlue(_, ty) => { write!(f, " - shim({:?})", ty) } - InstanceDef::BuiltinShim(_, ty) => { + InstanceDef::CloneShim(_, ty) => { write!(f, " - shim({:?})", ty) } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 81a0af63942..8c77dde0d25 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2228,7 +2228,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::DropGlue(..) | - ty::InstanceDef::BuiltinShim(..) => { + ty::InstanceDef::CloneShim(..) => { self.mir_shims(instance) } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 040d96b0dcc..818e713cf48 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -99,16 +99,15 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, ty::InstanceDef::DropGlue(def_id, ty) => { build_drop_shim(tcx, def_id, ty) } - ty::InstanceDef::BuiltinShim(def_id, ty) => { + ty::InstanceDef::CloneShim(def_id, ty) => { let name = tcx.item_name(def_id).as_str(); - let trait_id = tcx.trait_of_item(def_id); - if trait_id == tcx.lang_items.clone_trait() && name == "clone" { + if name == "clone" { build_clone_shim(tcx, def_id, ty) - } else if trait_id == tcx.lang_items.clone_trait() && name == "clone_from" { + } else if name == "clone_from" { debug!("make_shim({:?}: using default trait implementation", instance); return tcx.optimized_mir(def_id); } else { - bug!("builtin shim {:?} not supported", instance) + bug!("builtin clone shim {:?} not supported", instance) } } ty::InstanceDef::Intrinsic(_) => { @@ -272,10 +271,10 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } -/// Build a `Clone::clone` shim for `recvr_ty`. Here, `def_id` is `Clone::clone`. +/// Build a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`. fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - rcvr_ty: ty::Ty<'tcx>) + self_ty: ty::Ty<'tcx>) -> Mir<'tcx> { let sig = tcx.fn_sig(def_id); @@ -348,7 +347,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, loc }; - match rcvr_ty.sty { + match self_ty.sty { ty::TyArray(ty, len) => { let mut returns = Vec::new(); for i in 0..len { diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 4e02810b040..a6f7e931a67 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -700,7 +700,7 @@ fn visit_instance_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) | - ty::InstanceDef::BuiltinShim(..) => { + ty::InstanceDef::CloneShim(..) => { output.push(create_fn_trans_item(instance)); } } @@ -718,7 +718,7 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) | - ty::InstanceDef::BuiltinShim(..) => return true + ty::InstanceDef::CloneShim(..) => return true }; match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 401ee8cfaa8..b0d8be23b0d 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -143,9 +143,9 @@ fn resolve_associated_item<'a, 'tcx>( substs: rcvr_substs } } - traits::VtableBuiltin(..) => { + traits::VtableBuiltin(..) if Some(trait_id) == tcx.lang_items.clone_trait() => { Instance { - def: ty::InstanceDef::BuiltinShim(def_id, trait_ref.self_ty()), + def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), substs: rcvr_substs } } diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 0149714d0b7..26256fa78dd 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -362,7 +362,7 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>, InstanceDef::Intrinsic(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | - InstanceDef::BuiltinShim(..) => { + InstanceDef::CloneShim(..) => { bug!("partitioning: Encountered unexpected root translation item: {:?}", trans_item) @@ -605,7 +605,7 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) | - ty::InstanceDef::BuiltinShim(..) => return None + ty::InstanceDef::CloneShim(..) => return None }; // If this is a method, we want to put it into the same module as From df7be435d3a1ad05ba8b71b2966b4ec9b2a1229e Mon Sep 17 00:00:00 2001 From: scalexm Date: Mon, 7 Aug 2017 17:14:20 +0200 Subject: [PATCH 08/10] Optimize clone shim for `Copy` types --- src/librustc_mir/shim.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 818e713cf48..a9fa596a567 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -347,7 +347,20 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, loc }; + let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), span); + match self_ty.sty { + _ if is_copy => { + // `return *self;` + let statement = Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Use(Operand::Consume(rcvr)) + ) + }; + block(&mut blocks, statement, TerminatorKind::Return); + } ty::TyArray(ty, len) => { let mut returns = Vec::new(); for i in 0..len { @@ -402,15 +415,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, block(&mut blocks, statement, TerminatorKind::Return); } _ => { - // `return *self;` - let statement = Statement { - source_info: source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(rcvr)) - ) - }; - block(&mut blocks, statement, TerminatorKind::Return); + bug!("builtin shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); } }; From e1f1798ba90676d36906c016145f237e54eea294 Mon Sep 17 00:00:00 2001 From: scalexm Date: Tue, 8 Aug 2017 17:13:12 +0200 Subject: [PATCH 09/10] Handle unwinding, use a loop for arrays --- src/librustc_mir/shim.rs | 297 ++++++++++++++++++---- src/test/run-pass/builtin-clone-unwind.rs | 65 +++++ src/test/run-pass/builtin-clone.rs | 2 +- 3 files changed, 308 insertions(+), 56 deletions(-) create mode 100644 src/test/run-pass/builtin-clone-unwind.rs diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index a9fa596a567..d2a42650e3d 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -286,18 +286,26 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, let mut local_decls = local_decls_for_sig(&sig, span); let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let mut blocks = IndexVec::new(); - let block = |blocks: &mut IndexVec<_, _>, statement, kind| { + let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { blocks.push(BasicBlockData { - statements: vec![statement], + statements, terminator: Some(Terminator { source_info, kind }), - is_cleanup: false + is_cleanup, }) }; - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let make_lvalue = |mutability, ty, local_decls: &mut IndexVec<_, _>| { + Lvalue::Local( + local_decls.push(temp_decl(mutability, ty, span)) + ) + }; - let call_clone = |i, ty, rcvr_field, blocks: &mut _, local_decls: &mut IndexVec<_, _>| { + let call_clone = |ty, rcvr_field, next, cleanup, + blocks: &mut _, local_decls: &mut IndexVec<_, _>| + { // `func == Clone::clone(&ty) -> ty` let substs = Substs::for_item(tcx, def_id, |_, _| tcx.types.re_erased, |_, _| ty); let func = Operand::Constant(box Constant { @@ -308,28 +316,20 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, }, }); - let ref_loc = Lvalue::Local( - local_decls.push(temp_decl( - Mutability::Not, - tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { - ty, - mutbl: hir::Mutability::MutImmutable, - }), - span - )) + let ref_loc = make_lvalue( + Mutability::Not, + tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { + ty, + mutbl: hir::Mutability::MutImmutable, + }), + local_decls ); - let loc = Lvalue::Local( - local_decls.push(temp_decl( - Mutability::Not, - ty, - span - )) - ); + let loc = make_lvalue(Mutability::Not, ty, local_decls); // `let ref_loc: &ty = &rcvr_field;` let statement = Statement { - source_info: source_info, + source_info, kind: StatementKind::Assign( ref_loc.clone(), Rvalue::Ref(tcx.types.re_erased, BorrowKind::Shared, rcvr_field) @@ -337,73 +337,260 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, }; // `let loc = Clone::clone(ref_loc);` - block(blocks, statement, TerminatorKind::Call { + block(blocks, vec![statement], TerminatorKind::Call { func, args: vec![Operand::Consume(ref_loc)], - destination: Some((loc.clone(), BasicBlock::new(i + 1))), - cleanup: None, - }); + destination: Some((loc.clone(), next)), + cleanup: Some(cleanup), + }, false); loc }; let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), span); - match self_ty.sty { _ if is_copy => { // `return *self;` - let statement = Statement { - source_info: source_info, + let ret_statement = Statement { + source_info, kind: StatementKind::Assign( Lvalue::Local(RETURN_POINTER), Rvalue::Use(Operand::Consume(rcvr)) ) }; - block(&mut blocks, statement, TerminatorKind::Return); + block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); } ty::TyArray(ty, len) => { - let mut returns = Vec::new(); - for i in 0..len { - let index = ConstUsize::new(i as u64, tcx.sess.target.uint_type).unwrap(); - let rcvr_field = rcvr.clone().index( - Operand::Constant(box Constant { - span, - ty: tcx.types.usize, - literal: Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(index)) - } - }) + let make_loop = |beg, end, loop_body, loop_end, + blocks: &mut _, local_decls: &mut _, is_cleanup| + { + let cond = make_lvalue(Mutability::Mut, tcx.types.bool, local_decls); + let compute_cond = Statement { + source_info, + kind: StatementKind::Assign( + cond.clone(), + Rvalue::BinaryOp(BinOp::Ne, Operand::Consume(end), Operand::Consume(beg)) + ) + }; + + // `if end != beg { goto loop_body; } else { goto loop_end; }` + block( + blocks, + vec![compute_cond], + TerminatorKind::if_(tcx, Operand::Consume(cond), loop_body, loop_end), + is_cleanup ); + }; - // `returns[i] = Clone::clone(&rcvr[i]);` - returns.push(call_clone(i, ty, rcvr_field, &mut blocks, &mut local_decls)); - } + let make_usize = |value| { + let value = ConstUsize::new(value as u64, tcx.sess.target.uint_type).unwrap(); + box Constant { + span, + ty: tcx.types.usize, + literal: Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(value)) + } + } + }; - // `return [returns[0], returns[1], ..., returns[len - 1]];` - let statement = Statement { + let beg = make_lvalue(Mutability::Mut, tcx.types.usize, &mut local_decls); + let end = make_lvalue(Mutability::Not, tcx.types.usize, &mut local_decls); + let ret = make_lvalue(Mutability::Mut, tcx.mk_array(ty, len), &mut local_decls); + + // BB #0 + // `let mut beg = 0;` + // `let end = len;` + // `goto #1;` + let inits = vec![ + Statement { + source_info, + kind: StatementKind::Assign( + beg.clone(), + Rvalue::Use(Operand::Constant(make_usize(0))) + ) + }, + Statement { + source_info, + kind: StatementKind::Assign( + end.clone(), + Rvalue::Use(Operand::Constant(make_usize(len))) + ) + } + ]; + block(&mut blocks, inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); + + // BB #1: loop { + // BB #2; + // BB #3; + // } + // BB #4; + make_loop( + beg.clone(), + end, + BasicBlock::new(2), + BasicBlock::new(4), + &mut blocks, + &mut local_decls, + false + ); + + // BB #2 + // `let cloned = Clone::clone(rcvr[beg])`; + // Goto #3 if ok, #5 if unwinding happens. + let rcvr_field = rcvr.clone().index(Operand::Consume(beg.clone())); + let cloned = call_clone( + ty, + rcvr_field, + BasicBlock::new(3), + BasicBlock::new(5), + &mut blocks, + &mut local_decls + ); + + // BB #3 + // `ret[beg] = cloned;` + // `beg = beg + 1;` + // `goto #1`; + let ret_field = ret.clone().index(Operand::Consume(beg.clone())); + let statements = vec![ + Statement { + source_info, + kind: StatementKind::Assign( + ret_field, + Rvalue::Use(Operand::Consume(cloned)) + ) + }, + Statement { + source_info, + kind: StatementKind::Assign( + beg.clone(), + Rvalue::BinaryOp( + BinOp::Add, + Operand::Consume(beg.clone()), + Operand::Constant(make_usize(1)) + ) + ) + } + ]; + block( + &mut blocks, + statements, + TerminatorKind::Goto { target: BasicBlock::new(1) }, + false + ); + + // BB #4 + // `return ret;` + let ret_statement = Statement { source_info: source_info, kind: StatementKind::Assign( Lvalue::Local(RETURN_POINTER), - Rvalue::Aggregate( - box AggregateKind::Array(ty), - returns.into_iter().map(Operand::Consume).collect() + Rvalue::Use(Operand::Consume(ret.clone())), + ) + }; + block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); + + // BB #5 (cleanup) + // `let end = beg;` + // `let mut beg = 0;` + // goto #6; + let end = beg; + let beg = make_lvalue(Mutability::Mut, tcx.types.usize, &mut local_decls); + let init = Statement { + source_info, + kind: StatementKind::Assign( + beg.clone(), + Rvalue::Use(Operand::Constant(make_usize(0))) + ) + }; + block( + &mut blocks, + vec![init], + TerminatorKind::Goto { target: BasicBlock::new(6) }, + true + ); + + // BB #6 (cleanup): loop { + // BB #7; + // BB #8; + // } + // BB #9; + make_loop( + beg.clone(), + end, + BasicBlock::new(7), + BasicBlock::new(9), + &mut blocks, + &mut local_decls, + true + ); + + // BB #7 (cleanup) + // `drop(ret[beg])`; + block(&mut blocks, vec![], TerminatorKind::Drop { + location: ret.index(Operand::Consume(beg.clone())), + target: BasicBlock::new(8), + unwind: None, + }, true); + + // BB #8 (cleanup) + // `beg = beg + 1;` + // `goto #6;` + let statement = Statement { + source_info, + kind: StatementKind::Assign( + beg.clone(), + Rvalue::BinaryOp( + BinOp::Add, + Operand::Consume(beg.clone()), + Operand::Constant(make_usize(1)) ) ) }; - block(&mut blocks, statement, TerminatorKind::Return); + block( + &mut blocks, + vec![statement], + TerminatorKind::Goto { target: BasicBlock::new(6) }, + true + ); + + // BB #9 (resume) + block(&mut blocks, vec![], TerminatorKind::Resume, true); } ty::TyTuple(tys, _) => { let mut returns = Vec::new(); for (i, ity) in tys.iter().enumerate() { let rcvr_field = rcvr.clone().field(Field::new(i), *ity); + // BB #(2i) // `returns[i] = Clone::clone(&rcvr.i);` - returns.push(call_clone(i, *ity, rcvr_field, &mut blocks, &mut local_decls)); + // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens. + returns.push(call_clone( + *ity, + rcvr_field, + BasicBlock::new(2 * i + 2), + BasicBlock::new(2 * i + 1), + &mut blocks, + &mut local_decls + )); + + // BB #(2i + 1) (cleanup) + if i == 0 { + // Nothing to drop, just resume. + block(&mut blocks, vec![], TerminatorKind::Resume, true); + } else { + // Drop previous field and goto previous cleanup block. + block(&mut blocks, vec![], TerminatorKind::Drop { + location: returns[i - 1].clone(), + target: BasicBlock::new(2 * i - 1), + unwind: None, + }, true); + } } // `return (returns[0], returns[1], ..., returns[tys.len() - 1]);` - let statement = Statement { - source_info: source_info, + let ret_statement = Statement { + source_info, kind: StatementKind::Assign( Lvalue::Local(RETURN_POINTER), Rvalue::Aggregate( @@ -412,10 +599,10 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, ) ) }; - block(&mut blocks, statement, TerminatorKind::Return); + block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); } _ => { - bug!("builtin shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); + bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); } }; diff --git a/src/test/run-pass/builtin-clone-unwind.rs b/src/test/run-pass/builtin-clone-unwind.rs new file mode 100644 index 00000000000..2829f79812c --- /dev/null +++ b/src/test/run-pass/builtin-clone-unwind.rs @@ -0,0 +1,65 @@ +// Copyright 2017 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 builtin implementations of `Clone` cleanup everything +// in case of unwinding. + +use std::thread; +use std::sync::Arc; + +struct S(Arc<()>); + +impl Clone for S { + fn clone(&self) -> Self { + if Arc::strong_count(&self.0) == 7 { + panic!("oops"); + } + + S(self.0.clone()) + } +} + +fn main() { + let counter = Arc::new(()); + + // Unwinding with tuples... + let ccounter = counter.clone(); + let child = thread::spawn(move || { + let _ = ( + S(ccounter.clone()), + S(ccounter.clone()), + S(ccounter.clone()), + S(ccounter) + ).clone(); + }); + + assert!(child.join().is_err()); + assert_eq!( + 1, + Arc::strong_count(&counter) + ); + + // ... and with arrays. + let ccounter = counter.clone(); + let child = thread::spawn(move || { + let _ = [ + S(ccounter.clone()), + S(ccounter.clone()), + S(ccounter.clone()), + S(ccounter) + ].clone(); + }); + + assert!(child.join().is_err()); + assert_eq!( + 1, + Arc::strong_count(&counter) + ); +} diff --git a/src/test/run-pass/builtin-clone.rs b/src/test/run-pass/builtin-clone.rs index 87af48c13b0..95903610931 100644 --- a/src/test/run-pass/builtin-clone.rs +++ b/src/test/run-pass/builtin-clone.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // From d58b40e16b9466ba4e049fe0dae8b8d08df1d44d Mon Sep 17 00:00:00 2001 From: scalexm Date: Wed, 9 Aug 2017 13:55:27 +0200 Subject: [PATCH 10/10] Use an helper struct --- src/librustc_mir/shim.rs | 578 +++++++++++----------- src/test/run-pass/builtin-clone-unwind.rs | 20 +- 2 files changed, 308 insertions(+), 290 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index d2a42650e3d..f3c3c808446 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -277,67 +277,151 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, self_ty: ty::Ty<'tcx>) -> Mir<'tcx> { - let sig = tcx.fn_sig(def_id); - let sig = tcx.erase_late_bound_regions(&sig); - let span = tcx.def_span(def_id); - debug!("build_clone_shim(def_id={:?})", def_id); - let mut local_decls = local_decls_for_sig(&sig, span); - let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + let mut builder = CloneShimBuilder::new(tcx, def_id); + let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), builder.span); - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + match self_ty.sty { + _ if is_copy => builder.copy_shim(), + ty::TyArray(ty, len) => builder.array_shim(ty, len), + ty::TyTuple(tys, _) => builder.tuple_shim(tys), + _ => { + bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); + } + }; - let mut blocks = IndexVec::new(); - let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { - blocks.push(BasicBlockData { + builder.into_mir() +} + +struct CloneShimBuilder<'a, 'tcx: 'a> { + tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + local_decls: IndexVec>, + blocks: IndexVec>, + span: Span, + sig: ty::FnSig<'tcx>, +} + +impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { + fn new(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Self { + let sig = tcx.fn_sig(def_id); + let sig = tcx.erase_late_bound_regions(&sig); + let span = tcx.def_span(def_id); + + CloneShimBuilder { + tcx, + def_id, + local_decls: local_decls_for_sig(&sig, span), + blocks: IndexVec::new(), + span, + sig, + } + } + + fn into_mir(self) -> Mir<'tcx> { + Mir::new( + self.blocks, + IndexVec::from_elem_n( + VisibilityScopeData { span: self.span, parent_scope: None }, 1 + ), + IndexVec::new(), + self.sig.output(), + self.local_decls, + self.sig.inputs().len(), + vec![], + self.span + ) + } + + fn source_info(&self) -> SourceInfo { + SourceInfo { span: self.span, scope: ARGUMENT_VISIBILITY_SCOPE } + } + + fn block( + &mut self, + statements: Vec>, + kind: TerminatorKind<'tcx>, + is_cleanup: bool + ) -> BasicBlock { + let source_info = self.source_info(); + self.blocks.push(BasicBlockData { statements, terminator: Some(Terminator { source_info, kind }), is_cleanup, }) - }; + } - let make_lvalue = |mutability, ty, local_decls: &mut IndexVec<_, _>| { + fn make_statement(&self, kind: StatementKind<'tcx>) -> Statement<'tcx> { + Statement { + source_info: self.source_info(), + kind, + } + } + + fn copy_shim(&mut self) { + let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let ret_statement = self.make_statement( + StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Use(Operand::Consume(rcvr)) + ) + ); + self.block(vec![ret_statement], TerminatorKind::Return, false); + } + + fn make_lvalue(&mut self, mutability: Mutability, ty: ty::Ty<'tcx>) -> Lvalue<'tcx> { + let span = self.span; Lvalue::Local( - local_decls.push(temp_decl(mutability, ty, span)) + self.local_decls.push(temp_decl(mutability, ty, span)) ) - }; + } + + fn make_clone_call( + &mut self, + ty: ty::Ty<'tcx>, + rcvr_field: Lvalue<'tcx>, + next: BasicBlock, + cleanup: BasicBlock + ) -> Lvalue<'tcx> { + let tcx = self.tcx; + + let substs = Substs::for_item( + tcx, + self.def_id, + |_, _| tcx.types.re_erased, + |_, _| ty + ); - let call_clone = |ty, rcvr_field, next, cleanup, - blocks: &mut _, local_decls: &mut IndexVec<_, _>| - { // `func == Clone::clone(&ty) -> ty` - let substs = Substs::for_item(tcx, def_id, |_, _| tcx.types.re_erased, |_, _| ty); let func = Operand::Constant(box Constant { - span: span, - ty: tcx.mk_fn_def(def_id, substs), + span: self.span, + ty: tcx.mk_fn_def(self.def_id, substs), literal: Literal::Value { - value: ConstVal::Function(def_id, substs), + value: ConstVal::Function(self.def_id, substs), }, }); - let ref_loc = make_lvalue( + let ref_loc = self.make_lvalue( Mutability::Not, tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::MutImmutable, - }), - local_decls + }) ); - let loc = make_lvalue(Mutability::Not, ty, local_decls); + let loc = self.make_lvalue(Mutability::Not, ty); // `let ref_loc: &ty = &rcvr_field;` - let statement = Statement { - source_info, - kind: StatementKind::Assign( + let statement = self.make_statement( + StatementKind::Assign( ref_loc.clone(), Rvalue::Ref(tcx.types.re_erased, BorrowKind::Shared, rcvr_field) ) - }; + ); // `let loc = Clone::clone(ref_loc);` - block(blocks, vec![statement], TerminatorKind::Call { + self.block(vec![statement], TerminatorKind::Call { func, args: vec![Operand::Consume(ref_loc)], destination: Some((loc.clone(), next)), @@ -345,280 +429,214 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, }, false); loc - }; + } - let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), span); - match self_ty.sty { - _ if is_copy => { - // `return *self;` - let ret_statement = Statement { - source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(rcvr)) - ) - }; - block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); + fn loop_header( + &mut self, + beg: Lvalue<'tcx>, + end: Lvalue<'tcx>, + loop_body: BasicBlock, + loop_end: BasicBlock, + is_cleanup: bool + ) { + let tcx = self.tcx; + + let cond = self.make_lvalue(Mutability::Mut, tcx.types.bool); + let compute_cond = self.make_statement( + StatementKind::Assign( + cond.clone(), + Rvalue::BinaryOp(BinOp::Ne, Operand::Consume(end), Operand::Consume(beg)) + ) + ); + + // `if end != beg { goto loop_body; } else { goto loop_end; }` + self.block( + vec![compute_cond], + TerminatorKind::if_(tcx, Operand::Consume(cond), loop_body, loop_end), + is_cleanup + ); + } + + fn make_usize(&self, value: usize) -> Box> { + let value = ConstUsize::new(value as u64, self.tcx.sess.target.uint_type).unwrap(); + box Constant { + span: self.span, + ty: self.tcx.types.usize, + literal: Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(value)) + } } - ty::TyArray(ty, len) => { - let make_loop = |beg, end, loop_body, loop_end, - blocks: &mut _, local_decls: &mut _, is_cleanup| - { - let cond = make_lvalue(Mutability::Mut, tcx.types.bool, local_decls); - let compute_cond = Statement { - source_info, - kind: StatementKind::Assign( - cond.clone(), - Rvalue::BinaryOp(BinOp::Ne, Operand::Consume(end), Operand::Consume(beg)) - ) - }; + } - // `if end != beg { goto loop_body; } else { goto loop_end; }` - block( - blocks, - vec![compute_cond], - TerminatorKind::if_(tcx, Operand::Consume(cond), loop_body, loop_end), - is_cleanup - ); - }; + fn array_shim(&mut self, ty: ty::Ty<'tcx>, len: usize) { + let tcx = self.tcx; + let rcvr = Lvalue::Local(Local::new(1+0)).deref(); - let make_usize = |value| { - let value = ConstUsize::new(value as u64, tcx.sess.target.uint_type).unwrap(); - box Constant { - span, - ty: tcx.types.usize, - literal: Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(value)) - } - } - }; + let beg = self.make_lvalue(Mutability::Mut, tcx.types.usize); + let end = self.make_lvalue(Mutability::Not, tcx.types.usize); + let ret = self.make_lvalue(Mutability::Mut, tcx.mk_array(ty, len)); - let beg = make_lvalue(Mutability::Mut, tcx.types.usize, &mut local_decls); - let end = make_lvalue(Mutability::Not, tcx.types.usize, &mut local_decls); - let ret = make_lvalue(Mutability::Mut, tcx.mk_array(ty, len), &mut local_decls); - - // BB #0 - // `let mut beg = 0;` - // `let end = len;` - // `goto #1;` - let inits = vec![ - Statement { - source_info, - kind: StatementKind::Assign( - beg.clone(), - Rvalue::Use(Operand::Constant(make_usize(0))) - ) - }, - Statement { - source_info, - kind: StatementKind::Assign( - end.clone(), - Rvalue::Use(Operand::Constant(make_usize(len))) - ) - } - ]; - block(&mut blocks, inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - - // BB #1: loop { - // BB #2; - // BB #3; - // } - // BB #4; - make_loop( - beg.clone(), - end, - BasicBlock::new(2), - BasicBlock::new(4), - &mut blocks, - &mut local_decls, - false - ); - - // BB #2 - // `let cloned = Clone::clone(rcvr[beg])`; - // Goto #3 if ok, #5 if unwinding happens. - let rcvr_field = rcvr.clone().index(Operand::Consume(beg.clone())); - let cloned = call_clone( - ty, - rcvr_field, - BasicBlock::new(3), - BasicBlock::new(5), - &mut blocks, - &mut local_decls - ); - - // BB #3 - // `ret[beg] = cloned;` - // `beg = beg + 1;` - // `goto #1`; - let ret_field = ret.clone().index(Operand::Consume(beg.clone())); - let statements = vec![ - Statement { - source_info, - kind: StatementKind::Assign( - ret_field, - Rvalue::Use(Operand::Consume(cloned)) - ) - }, - Statement { - source_info, - kind: StatementKind::Assign( - beg.clone(), - Rvalue::BinaryOp( - BinOp::Add, - Operand::Consume(beg.clone()), - Operand::Constant(make_usize(1)) - ) - ) - } - ]; - block( - &mut blocks, - statements, - TerminatorKind::Goto { target: BasicBlock::new(1) }, - false - ); - - // BB #4 - // `return ret;` - let ret_statement = Statement { - source_info: source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(ret.clone())), - ) - }; - block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); - - // BB #5 (cleanup) - // `let end = beg;` - // `let mut beg = 0;` - // goto #6; - let end = beg; - let beg = make_lvalue(Mutability::Mut, tcx.types.usize, &mut local_decls); - let init = Statement { - source_info, - kind: StatementKind::Assign( + // BB #0 + // `let mut beg = 0;` + // `let end = len;` + // `goto #1;` + let inits = vec![ + self.make_statement( + StatementKind::Assign( beg.clone(), - Rvalue::Use(Operand::Constant(make_usize(0))) + Rvalue::Use(Operand::Constant(self.make_usize(0))) ) - }; - block( - &mut blocks, - vec![init], - TerminatorKind::Goto { target: BasicBlock::new(6) }, - true - ); + ), + self.make_statement( + StatementKind::Assign( + end.clone(), + Rvalue::Use(Operand::Constant(self.make_usize(len))) + ) + ) + ]; + self.block(inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - // BB #6 (cleanup): loop { - // BB #7; - // BB #8; - // } - // BB #9; - make_loop( - beg.clone(), - end, - BasicBlock::new(7), - BasicBlock::new(9), - &mut blocks, - &mut local_decls, - true - ); + // BB #1: loop { + // BB #2; + // BB #3; + // } + // BB #4; + self.loop_header(beg.clone(), end, BasicBlock::new(2), BasicBlock::new(4), false); - // BB #7 (cleanup) - // `drop(ret[beg])`; - block(&mut blocks, vec![], TerminatorKind::Drop { - location: ret.index(Operand::Consume(beg.clone())), - target: BasicBlock::new(8), - unwind: None, - }, true); + // BB #2 + // `let cloned = Clone::clone(rcvr[beg])`; + // Goto #3 if ok, #5 if unwinding happens. + let rcvr_field = rcvr.clone().index(Operand::Consume(beg.clone())); + let cloned = self.make_clone_call(ty, rcvr_field, BasicBlock::new(3), BasicBlock::new(5)); - // BB #8 (cleanup) - // `beg = beg + 1;` - // `goto #6;` - let statement = Statement { - source_info, - kind: StatementKind::Assign( + // BB #3 + // `ret[beg] = cloned;` + // `beg = beg + 1;` + // `goto #1`; + let ret_field = ret.clone().index(Operand::Consume(beg.clone())); + let statements = vec![ + self.make_statement( + StatementKind::Assign( + ret_field, + Rvalue::Use(Operand::Consume(cloned)) + ) + ), + self.make_statement( + StatementKind::Assign( beg.clone(), Rvalue::BinaryOp( BinOp::Add, Operand::Consume(beg.clone()), - Operand::Constant(make_usize(1)) + Operand::Constant(self.make_usize(1)) ) ) - }; - block( - &mut blocks, - vec![statement], - TerminatorKind::Goto { target: BasicBlock::new(6) }, - true - ); + ) + ]; + self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - // BB #9 (resume) - block(&mut blocks, vec![], TerminatorKind::Resume, true); - } - ty::TyTuple(tys, _) => { - let mut returns = Vec::new(); - for (i, ity) in tys.iter().enumerate() { - let rcvr_field = rcvr.clone().field(Field::new(i), *ity); + // BB #4 + // `return ret;` + let ret_statement = self.make_statement( + StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Use(Operand::Consume(ret.clone())), + ) + ); + self.block(vec![ret_statement], TerminatorKind::Return, false); - // BB #(2i) - // `returns[i] = Clone::clone(&rcvr.i);` - // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens. - returns.push(call_clone( + // BB #5 (cleanup) + // `let end = beg;` + // `let mut beg = 0;` + // goto #6; + let end = beg; + let beg = self.make_lvalue(Mutability::Mut, tcx.types.usize); + let init = self.make_statement( + StatementKind::Assign( + beg.clone(), + Rvalue::Use(Operand::Constant(self.make_usize(0))) + ) + ); + self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); + + // BB #6 (cleanup): loop { + // BB #7; + // BB #8; + // } + // BB #9; + self.loop_header(beg.clone(), end, BasicBlock::new(7), BasicBlock::new(9), true); + + // BB #7 (cleanup) + // `drop(ret[beg])`; + self.block(vec![], TerminatorKind::Drop { + location: ret.index(Operand::Consume(beg.clone())), + target: BasicBlock::new(8), + unwind: None, + }, true); + + // BB #8 (cleanup) + // `beg = beg + 1;` + // `goto #6;` + let statement = self.make_statement( + StatementKind::Assign( + beg.clone(), + Rvalue::BinaryOp( + BinOp::Add, + Operand::Consume(beg.clone()), + Operand::Constant(self.make_usize(1)) + ) + ) + ); + self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); + + // BB #9 (resume) + self.block(vec![], TerminatorKind::Resume, true); + } + + fn tuple_shim(&mut self, tys: &ty::Slice>) { + let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + + let mut returns = Vec::new(); + for (i, ity) in tys.iter().enumerate() { + let rcvr_field = rcvr.clone().field(Field::new(i), *ity); + + // BB #(2i) + // `returns[i] = Clone::clone(&rcvr.i);` + // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens. + returns.push( + self.make_clone_call( *ity, rcvr_field, BasicBlock::new(2 * i + 2), BasicBlock::new(2 * i + 1), - &mut blocks, - &mut local_decls - )); - - // BB #(2i + 1) (cleanup) - if i == 0 { - // Nothing to drop, just resume. - block(&mut blocks, vec![], TerminatorKind::Resume, true); - } else { - // Drop previous field and goto previous cleanup block. - block(&mut blocks, vec![], TerminatorKind::Drop { - location: returns[i - 1].clone(), - target: BasicBlock::new(2 * i - 1), - unwind: None, - }, true); - } - } - - // `return (returns[0], returns[1], ..., returns[tys.len() - 1]);` - let ret_statement = Statement { - source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Aggregate( - box AggregateKind::Tuple, - returns.into_iter().map(Operand::Consume).collect() - ) ) - }; - block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); - } - _ => { - bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); - } - }; + ); - let mir = Mir::new( - blocks, - IndexVec::from_elem_n( - VisibilityScopeData { span: span, parent_scope: None }, 1 - ), - IndexVec::new(), - sig.output(), - local_decls, - sig.inputs().len(), - vec![], - span - ); - mir + // BB #(2i + 1) (cleanup) + if i == 0 { + // Nothing to drop, just resume. + self.block(vec![], TerminatorKind::Resume, true); + } else { + // Drop previous field and goto previous cleanup block. + self.block(vec![], TerminatorKind::Drop { + location: returns[i - 1].clone(), + target: BasicBlock::new(2 * i - 1), + unwind: None, + }, true); + } + } + + // `return (returns[0], returns[1], ..., returns[tys.len() - 1]);` + let ret_statement = self.make_statement( + StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Aggregate( + box AggregateKind::Tuple, + returns.into_iter().map(Operand::Consume).collect() + ) + ) + ); + self.block(vec![ret_statement], TerminatorKind::Return, false); + } } /// Build a "call" shim for `def_id`. The shim calls the diff --git a/src/test/run-pass/builtin-clone-unwind.rs b/src/test/run-pass/builtin-clone-unwind.rs index 2829f79812c..90a41135286 100644 --- a/src/test/run-pass/builtin-clone-unwind.rs +++ b/src/test/run-pass/builtin-clone-unwind.rs @@ -12,13 +12,13 @@ // in case of unwinding. use std::thread; -use std::sync::Arc; +use std::rc::Rc; -struct S(Arc<()>); +struct S(Rc<()>); impl Clone for S { fn clone(&self) -> Self { - if Arc::strong_count(&self.0) == 7 { + if Rc::strong_count(&self.0) == 7 { panic!("oops"); } @@ -27,11 +27,11 @@ impl Clone for S { } fn main() { - let counter = Arc::new(()); + let counter = Rc::new(()); // Unwinding with tuples... let ccounter = counter.clone(); - let child = thread::spawn(move || { + let result = std::panic::catch_unwind(move || { let _ = ( S(ccounter.clone()), S(ccounter.clone()), @@ -40,15 +40,15 @@ fn main() { ).clone(); }); - assert!(child.join().is_err()); + assert!(result.is_err()); assert_eq!( 1, - Arc::strong_count(&counter) + Rc::strong_count(&counter) ); // ... and with arrays. let ccounter = counter.clone(); - let child = thread::spawn(move || { + let child = std::panic::catch_unwind(move || { let _ = [ S(ccounter.clone()), S(ccounter.clone()), @@ -57,9 +57,9 @@ fn main() { ].clone(); }); - assert!(child.join().is_err()); + assert!(result.is_err()); assert_eq!( 1, - Arc::strong_count(&counter) + Rc::strong_count(&counter) ); }