diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 6ec0a46ebf452..d79f38e097fbd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -119,6 +119,11 @@ pub struct InherentAssocCandidate { pub scope: DefId, } +pub struct ResolvedStructPath<'tcx> { + pub res: Result, + pub ty: Ty<'tcx>, +} + /// A context which can lower type-system entities from the [HIR][hir] to /// the [`rustc_middle::ty`] representation. /// @@ -2618,38 +2623,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_error(tcx, e) }; - let (ty, variant_did) = match qpath { + let ResolvedStructPath { res: opt_res, ty } = + self.lower_path_for_struct_expr(qpath, span, hir_id); + + let variant_did = match qpath { hir::QPath::Resolved(maybe_qself, path) => { debug!(?maybe_qself, ?path); - let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); - let ty = - self.lower_resolved_ty_path(opt_self_ty, path, hir_id, PermitVariants::Yes); let variant_did = match path.res { Res::Def(DefKind::Variant | DefKind::Struct, did) => did, _ => return non_adt_or_variant_res(), }; - (ty, variant_did) + variant_did } hir::QPath::TypeRelative(hir_self_ty, segment) => { debug!(?hir_self_ty, ?segment); - let self_ty = self.lower_ty(hir_self_ty); - let opt_res = self.lower_type_relative_ty_path( - self_ty, - hir_self_ty, - segment, - hir_id, - span, - PermitVariants::Yes, - ); - let (ty, _, res_def_id) = match opt_res { - Ok(r @ (_, DefKind::Variant | DefKind::Struct, _)) => r, + let res_def_id = match opt_res { + Ok(r) + if matches!( + tcx.def_kind(r.def_id()), + DefKind::Variant | DefKind::Struct + ) => + { + r.def_id() + } Ok(_) => return non_adt_or_variant_res(), Err(e) => return ty::Const::new_error(tcx, e), }; - (ty, res_def_id) + res_def_id } }; @@ -2710,6 +2713,41 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_value(tcx, valtree, ty) } + pub fn lower_path_for_struct_expr( + &self, + qpath: hir::QPath<'tcx>, + path_span: Span, + hir_id: HirId, + ) -> ResolvedStructPath<'tcx> { + match qpath { + hir::QPath::Resolved(ref maybe_qself, path) => { + let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); + let ty = self.lower_resolved_ty_path(self_ty, path, hir_id, PermitVariants::Yes); + ResolvedStructPath { res: Ok(path.res), ty } + } + hir::QPath::TypeRelative(hir_self_ty, segment) => { + let self_ty = self.lower_ty(hir_self_ty); + + let result = self.lower_type_relative_ty_path( + self_ty, + hir_self_ty, + segment, + hir_id, + path_span, + PermitVariants::Yes, + ); + let ty = result + .map(|(ty, _, _)| ty) + .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); + + ResolvedStructPath { + res: result.map(|(_, kind, def_id)| Res::Def(kind, def_id)), + ty, + } + } + } + } + /// Lower a [resolved][hir::QPath::Resolved] path to a (type-level) constant. fn lower_resolved_const_path( &self, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8c1cc5c17958e..d2b2bf01d8aa4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -13,7 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, LangItem, Node, QPath, is_range_literal}; use rustc_hir_analysis::check::potentially_plural_count; -use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; +use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath}; use rustc_index::IndexVec; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace}; use rustc_middle::ty::adjustment::AllowTwoPhase; @@ -1265,38 +1265,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path_span: Span, hir_id: HirId, ) -> (Res, LoweredTy<'tcx>) { + let ResolvedStructPath { res: result, ty } = + self.lowerer().lower_path_for_struct_expr(*qpath, path_span, hir_id); match *qpath { - QPath::Resolved(ref maybe_qself, path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw); - let ty = self.lowerer().lower_resolved_ty_path( - self_ty, - path, - hir_id, - PermitVariants::Yes, - ); - (path.res, LoweredTy::from_raw(self, path_span, ty)) - } - QPath::TypeRelative(hir_self_ty, segment) => { - let self_ty = self.lower_ty(hir_self_ty); - - let result = self.lowerer().lower_type_relative_ty_path( - self_ty.raw, - hir_self_ty, - segment, - hir_id, - path_span, - PermitVariants::Yes, - ); - let ty = result - .map(|(ty, _, _)| ty) - .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); + QPath::Resolved(_, path) => (path.res, LoweredTy::from_raw(self, path_span, ty)), + QPath::TypeRelative(_, _) => { let ty = LoweredTy::from_raw(self, path_span, ty); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); + let resolution = + result.map(|res: Res| (self.tcx().def_kind(res.def_id()), res.def_id())); // Write back the new resolution. - self.write_resolution(hir_id, result); + self.write_resolution(hir_id, resolution); - (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) + (result.unwrap_or(Res::Err), ty) } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 22762175bbea8..da6552b7df64f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -882,55 +882,46 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let trait_name = self.tcx.item_name(trait_did); if self.tcx.is_const_trait(trait_did) && !self.tcx.is_const_trait_impl(impl_did) { - if let Some(impl_did) = impl_did.as_local() - && let item = self.tcx.hir_expect_item(impl_did) - && let hir::ItemKind::Impl(item) = item.kind - && let Some(of_trait) = item.of_trait - { - // trait is const, impl is local and not const - diag.span_suggestion_verbose( - of_trait.trait_ref.path.span.shrink_to_lo(), - format!("make the `impl` of trait `{trait_name}` `const`"), - "const ".to_string(), - Applicability::MaybeIncorrect, - ); - } else { + if !impl_did.is_local() { diag.span_note( impl_span, format!("trait `{trait_name}` is implemented but not `const`"), ); + } - let (condition_options, format_args) = self.on_unimplemented_components( + if let Some(command) = + find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()) + .flatten() + { + let (_, format_args) = self.on_unimplemented_components( trait_ref, main_obligation, diag.long_ty_path(), ); + let CustomDiagnostic { message, label, notes, parent_label: _ } = + command.eval(None, &format_args); - if let Some(command) = find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()).flatten(){ - let note = command.eval( - Some(&condition_options), - &format_args, - ); - let CustomDiagnostic { - message, - label, - notes, - parent_label, - } = note; - - if let Some(message) = message { - diag.primary_message(message); - } - if let Some(label) = label { - diag.span_label(impl_span, label); - } - for note in notes { - diag.note(note); - } - if let Some(parent_label) = parent_label { - diag.span_label(impl_span, parent_label); - } + if let Some(message) = message { + diag.primary_message(message); + } + if let Some(label) = label { + diag.span_label(span, label); + } + for note in notes { + diag.note(note); } + } else if let Some(impl_did) = impl_did.as_local() + && let item = self.tcx.hir_expect_item(impl_did) + && let hir::ItemKind::Impl(item) = item.kind + && let Some(of_trait) = item.of_trait + { + // trait is const, impl is local and not const + diag.span_suggestion_verbose( + of_trait.trait_ref.path.span.shrink_to_lo(), + format!("make the `impl` of trait `{trait_name}` `const`"), + "const ".to_string(), + Applicability::MaybeIncorrect, + ); } } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index cbb801bd6d736..af76fddaced7c 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -311,6 +311,12 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { #[rustc_diagnostic_item = "Rc"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] +#[diagnostic::on_move( + message = "the type `{Self}` does not implement `Copy`", + label = "this move could be avoided by cloning the original `{Self}`, which is inexpensive", + note = "consider using `Rc::clone`" +)] + pub struct Rc< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5366a0eca3293..2d0ce4f56c65f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -176,6 +176,35 @@ fn is_glob_import(tcx: TyCtxt<'_>, import_id: LocalDefId) -> bool { } } +/// Returns true if `def_id` is a macro and should be inlined. +pub(crate) fn macro_reexport_is_inline( + tcx: TyCtxt<'_>, + import_id: LocalDefId, + def_id: DefId, +) -> bool { + if !matches!(tcx.def_kind(def_id), DefKind::Macro(MacroKinds::BANG)) { + return false; + } + + for reexport_def_id in reexport_chain(tcx, import_id, def_id).iter().flat_map(|r| r.id()) { + let is_hidden = tcx.is_doc_hidden(reexport_def_id); + let is_inline = find_attr!( + inline::load_attrs(tcx, reexport_def_id), + Doc(d) + if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) + ); + + // hidden takes absolute priority over inline on the same node + if is_hidden { + return false; + } + if is_inline { + return true; + } + } + false +} + fn generate_item_with_correct_attrs( cx: &mut DocContext<'_>, kind: ItemKind, @@ -201,7 +230,8 @@ fn generate_item_with_correct_attrs( Doc(d) if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) ) || (is_glob_import(tcx, import_id) - && (cx.document_hidden() || !tcx.is_doc_hidden(def_id))); + && (cx.document_hidden() || !tcx.is_doc_hidden(def_id))) + || macro_reexport_is_inline(tcx, import_id, def_id); attrs.extend(get_all_import_attributes(cx, import_id, def_id, is_inline)); is_inline = is_inline || import_is_inline; } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 906289ba755e3..fda03563c79f2 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -478,11 +478,21 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // If there was a private module in the current path then don't bother inlining // anything as it will probably be stripped anyway. if is_pub && self.inside_public_path { - let please_inline = find_attr!( - attrs, - Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) - ); + let please_inline = if let Some(res_did) = res.opt_def_id() + && matches!(tcx.def_kind(res_did), DefKind::Macro(MacroKinds::BANG)) + { + crate::clean::macro_reexport_is_inline( + tcx, + item.owner_id.def_id, + res_did, + ) + } else { + find_attr!( + attrs, + Doc(d) + if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) + ) + }; let ident = match kind { hir::UseKind::Single(ident) => Some(ident.name), hir::UseKind::Glob => None, diff --git a/tests/rustdoc-html/macro-reexport-inline.rs b/tests/rustdoc-html/macro-reexport-inline.rs new file mode 100644 index 0000000000000..4707d958f8c97 --- /dev/null +++ b/tests/rustdoc-html/macro-reexport-inline.rs @@ -0,0 +1,129 @@ +// Regression test for . +// The goal is to ensure that declarative macros re-exported by name +// inherit the `#[doc(inline)]` attribute from intermediate re-exports, +// matching the behavior of glob re-exports. + +#![crate_name = "foo"] + +#[macro_use] +mod macros { + #[macro_export] + #[doc(hidden)] + macro_rules! explicit_macro { + () => {}; + } + + #[macro_export] + #[doc(hidden)] + macro_rules! wild_macro { + () => {}; + } + + #[macro_export] + #[doc(hidden)] + macro_rules! actually_hidden_macro { + () => {}; + } + + #[macro_export] + #[doc(hidden)] + macro_rules! actually_hidden_wild_macro { + () => {}; + } + + #[macro_export] + #[doc(hidden)] + macro_rules! actually_hidden_indirect_macro { + () => {}; + } +} + +// Standard items (like structs) are provided as control cases to ensure +// macro inlining behavior maintains parity. +#[doc(hidden)] +pub struct HiddenStruct; + +#[doc(hidden)] +pub struct IndirectlyHiddenStruct; + +pub mod bar { + mod hidden_explicit { + #[doc(inline)] + pub use crate::explicit_macro; + } + + mod hidden_wild { + #[doc(inline)] + pub use crate::wild_macro; + } + + mod actually_hidden { + // BUG: as demonstrated by the `actually_hidden_struct` module, when both + // `doc(hidden)` and `doc(inline)` are specified, `doc(hidden)` + // should take priority. + #[doc(hidden)] + #[doc(inline)] + pub use crate::actually_hidden_macro; + } + + mod actually_hidden_indirect_inner { + #[doc(inline)] + pub use crate::actually_hidden_indirect_macro; + } + + mod actually_hidden_indirect { + // BUG: when there is a chain of imports, we should stop looking as soon as soon as we hit + // something with `doc(hidden)`. + #[doc(hidden)] + pub use super::actually_hidden_indirect_inner::actually_hidden_indirect_macro; + } + + mod actually_hidden_indirect_struct_inner { + #[doc(inline)] + pub use crate::IndirectlyHiddenStruct; + } + + mod actually_hidden_indirect_struct { + #[doc(hidden)] + pub use super::actually_hidden_indirect_struct_inner::IndirectlyHiddenStruct; + } + + mod actually_hidden_wild { + #[doc(hidden)] + #[doc(inline)] + pub use crate::actually_hidden_wild_macro; + } + + mod actually_hidden_struct { + #[doc(inline)] + #[doc(hidden)] + pub use crate::HiddenStruct; + } + + // First, we check that the explicitly named macro inherits the inline attribute + // from `hidden_explicit` and is successfully rendered. + //@ has 'foo/bar/macro.explicit_macro.html' + //@ has 'foo/bar/index.html' '//a[@href="macro.explicit_macro.html"]' 'explicit_macro' + pub use self::hidden_explicit::explicit_macro; + + // Next, we ensure that the glob-imported macro continues to render correctly + // as a control case. + //@ has 'foo/bar/macro.wild_macro.html' + //@ has 'foo/bar/index.html' '//a[@href="macro.wild_macro.html"]' 'wild_macro' + pub use self::hidden_wild::*; + + //@ !has 'foo/bar/macro.actually_hidden_macro.html' + pub use self::actually_hidden::actually_hidden_macro; + + //@ !has 'foo/bar/macro.actually_hidden_wild_macro.html' + pub use self::actually_hidden_wild::*; + + //@ !has 'foo/bar/struct.HiddenStruct.html' + pub use self::actually_hidden_struct::HiddenStruct; + + //@ !has 'foo/bar/macro.actually_hidden_indirect_macro.html' + pub use self::actually_hidden_indirect::actually_hidden_indirect_macro; + + //@ !has 'foo/bar/struct.IndirectlyHiddenStruct.html' + pub use self::actually_hidden_indirect_struct::IndirectlyHiddenStruct; +} diff --git a/tests/ui/diagnostic_namespace/on_const/auxiliary/const_trait.rs b/tests/ui/diagnostic_namespace/on_const/auxiliary/const_trait.rs deleted file mode 100644 index cf854a9072a6a..0000000000000 --- a/tests/ui/diagnostic_namespace/on_const/auxiliary/const_trait.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(diagnostic_on_const)] - -pub struct X; - -#[diagnostic::on_const(message = "message", label = "label", note = "note")] -impl PartialEq for X { - fn eq(&self, _other: &X) -> bool { - true - } -} diff --git a/tests/ui/diagnostic_namespace/on_const/auxiliary/non_const_impl.rs b/tests/ui/diagnostic_namespace/on_const/auxiliary/non_const_impl.rs new file mode 100644 index 0000000000000..3501d0ee9ec57 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/auxiliary/non_const_impl.rs @@ -0,0 +1,15 @@ +#![feature(diagnostic_on_const)] + +pub struct X; + +#[diagnostic::on_const( + message = "their message", + label = "their label", + note = "their note", + note = "their other note" +)] +impl PartialEq for X { + fn eq(&self, _other: &X) -> bool { + true + } +} diff --git a/tests/ui/diagnostic_namespace/on_const/it_works_foreign.rs b/tests/ui/diagnostic_namespace/on_const/it_works_foreign.rs new file mode 100644 index 0000000000000..0888ee096ff4f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/it_works_foreign.rs @@ -0,0 +1,16 @@ +//@ aux-build: non_const_impl.rs +#![crate_type = "lib"] + +extern crate non_const_impl; + +use non_const_impl::X; + +const _: () = { + let x = X; + x == x; + //~^ ERROR: their message + //~| NOTE: their label + //~| NOTE: trait `PartialEq` is implemented but not `const` + //~| NOTE: their note + //~| NOTE: their other note +}; diff --git a/tests/ui/diagnostic_namespace/on_const/it_works_foreign.stderr b/tests/ui/diagnostic_namespace/on_const/it_works_foreign.stderr new file mode 100644 index 0000000000000..ad9b2364f21f3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/it_works_foreign.stderr @@ -0,0 +1,17 @@ +error[E0277]: their message + --> $DIR/it_works_foreign.rs:10:5 + | +LL | x == x; + | ^^^^^^ their label + | +note: trait `PartialEq` is implemented but not `const` + --> $DIR/auxiliary/non_const_impl.rs:11:1 + | +LL | impl PartialEq for X { + | ^^^^^^^^^^^^^^^^^^^^ + = note: their note + = note: their other note + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_const/it_works_local.rs b/tests/ui/diagnostic_namespace/on_const/it_works_local.rs new file mode 100644 index 0000000000000..176fe8d01d353 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/it_works_local.rs @@ -0,0 +1,25 @@ +#![crate_type = "lib"] +#![feature(diagnostic_on_const)] + +pub struct X; + +#[diagnostic::on_const( + message = "my message", + label = "my label", + note = "my note", + note = "my other note" +)] +impl PartialEq for X { + fn eq(&self, _other: &X) -> bool { + true + } +} + +const _: () = { + let x = X; + x == x; + //~^ ERROR: my message + //~| NOTE: my label + //~| NOTE: my note + //~| NOTE: my other note +}; diff --git a/tests/ui/diagnostic_namespace/on_const/it_works_local.stderr b/tests/ui/diagnostic_namespace/on_const/it_works_local.stderr new file mode 100644 index 0000000000000..0db90fac1639e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/it_works_local.stderr @@ -0,0 +1,12 @@ +error[E0277]: my message + --> $DIR/it_works_local.rs:20:5 + | +LL | x == x; + | ^^^^^^ my label + | + = note: my note + = note: my other note + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/moves/move-fn-self-receiver.rs b/tests/ui/moves/move-fn-self-receiver.rs index 946642ef6f3ad..5079ae66ee14d 100644 --- a/tests/ui/moves/move-fn-self-receiver.rs +++ b/tests/ui/moves/move-fn-self-receiver.rs @@ -52,7 +52,7 @@ fn move_out(val: Container) { let rc_foo = Rc::new(Foo); rc_foo.use_rc_self(); - rc_foo; //~ ERROR use of moved + rc_foo; //~ ERROR the type `Rc` does not implement `Copy` let foo_add = Foo; foo_add + Foo; diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr index de19a99d388ca..40a82523c840c 100644 --- a/tests/ui/moves/move-fn-self-receiver.stderr +++ b/tests/ui/moves/move-fn-self-receiver.stderr @@ -102,16 +102,17 @@ LL | mut_foo; LL | ret; | --- borrow later used here -error[E0382]: use of moved value: `rc_foo` +error[E0382]: the type `Rc` does not implement `Copy` --> $DIR/move-fn-self-receiver.rs:55:5 | LL | let rc_foo = Rc::new(Foo); - | ------ move occurs because `rc_foo` has type `Rc`, which does not implement the `Copy` trait + | ------ this move could be avoided by cloning the original `Rc`, which is inexpensive LL | rc_foo.use_rc_self(); | ------------- `rc_foo` moved due to this method call LL | rc_foo; | ^^^^^^ value used here after move | + = note: consider using `Rc::clone` note: `Foo::use_rc_self` takes ownership of the receiver `self`, which moves `rc_foo` --> $DIR/move-fn-self-receiver.rs:16:20 | diff --git a/tests/ui/moves/use_of_moved_value_clone_suggestions.rs b/tests/ui/moves/use_of_moved_value_clone_suggestions.rs index 9a0a397a73387..b31fe507b887a 100644 --- a/tests/ui/moves/use_of_moved_value_clone_suggestions.rs +++ b/tests/ui/moves/use_of_moved_value_clone_suggestions.rs @@ -1,6 +1,6 @@ // `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - (t, t) //~ ERROR use of moved value: `t` + (t, t) //~ ERROR the type `Rc` does not implement `Copy` } fn main() {} diff --git a/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr b/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr index 785329565eb9b..a9e554b6e1946 100644 --- a/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr +++ b/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr @@ -1,13 +1,14 @@ -error[E0382]: use of moved value: `t` +error[E0382]: the type `Rc` does not implement `Copy` --> $DIR/use_of_moved_value_clone_suggestions.rs:3:9 | LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait + | - this move could be avoided by cloning the original `Rc`, which is inexpensive LL | (t, t) | - ^ value used here after move | | | value moved here | + = note: consider using `Rc::clone` help: clone the value to increment its reference count | LL | (t.clone(), t)