diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 9540ade194327..cef45435cb88a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -25,6 +25,7 @@ pub(crate) mod debug; pub(crate) mod default; pub(crate) mod from; pub(crate) mod hash; +pub(crate) mod reborrow; #[path = "cmp/eq.rs"] pub(crate) mod eq; diff --git a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs new file mode 100644 index 0000000000000..9dc1ccf4fd8e6 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs @@ -0,0 +1,235 @@ +use rustc_ast::{ + self as ast, AttrArgs, GenericArg, GenericParamKind, Generics, ItemKind, MetaItem, token, +}; +use rustc_errors::E0802; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_macros::Diagnostic; +use rustc_span::{Ident, Span, Symbol, sym}; +use thin_vec::ThinVec; + +macro_rules! path { + ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } +} + +pub(crate) fn expand_deriving_reborrow( + cx: &ExtCtxt<'_>, + span: Span, + _mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), + _is_const: bool, +) { + let Some((ident, generics)) = struct_def(cx, span, item, sym::Reborrow) else { + return; + }; + + push_marker_impl(cx, span, ident, generics, sym::Reborrow, Vec::new(), push); +} + +pub(crate) fn expand_deriving_coerce_shared( + cx: &ExtCtxt<'_>, + span: Span, + _mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), + _is_const: bool, +) { + let Some((ident, generics)) = struct_def(cx, span, item, sym::CoerceShared) else { + return; + }; + let Some(target) = coerce_shared_target(cx, span, item) else { + return; + }; + + push_marker_impl( + cx, + span, + ident, + generics, + sym::CoerceShared, + vec![GenericArg::Type(target)], + push, + ); +} + +fn struct_def<'a>( + cx: &ExtCtxt<'_>, + span: Span, + item: &'a Annotatable, + trait_name: Symbol, +) -> Option<(Ident, &'a Generics)> { + match item { + Annotatable::Item(item) => match &item.kind { + ItemKind::Struct(ident, generics, _) => Some((*ident, generics)), + ItemKind::Enum(..) => { + cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "enum" }); + None + } + ItemKind::Union(..) => { + cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "union" }); + None + } + _ => { + cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" }); + None + } + }, + _ => { + cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" }); + None + } + } +} + +fn coerce_shared_target(cx: &ExtCtxt<'_>, span: Span, item: &Annotatable) -> Option> { + let Annotatable::Item(item) = item else { + cx.dcx().emit_err(MissingTarget { span }); + return None; + }; + + let mut attrs = item.attrs.iter().filter(|attr| attr.has_name(sym::coerce_shared)); + let Some(attr) = attrs.next() else { + cx.dcx().emit_err(MissingTarget { span }); + return None; + }; + if let Some(duplicate) = attrs.next() { + cx.dcx().emit_err(DuplicateTarget { first: attr.span, duplicate: duplicate.span }); + return None; + } + + let AttrArgs::Delimited(args) = &attr.get_normal_item().args else { + cx.dcx().emit_err(MalformedTarget { span: attr.span }); + return None; + }; + if args.delim != token::Delimiter::Parenthesis || args.tokens.is_empty() { + cx.dcx().emit_err(MalformedTarget { span: attr.span }); + return None; + } + + let mut parser = cx.new_parser_from_tts(args.tokens.clone()); + let target = match parser.parse_ty() { + Ok(target) => target, + Err(err) => { + err.cancel(); + cx.dcx().emit_err(MalformedTarget { span: attr.span }); + return None; + } + }; + if parser.token != token::Eof { + cx.dcx().emit_err(MalformedTarget { span: attr.span }); + return None; + } + + Some(target) +} + +fn push_marker_impl( + cx: &ExtCtxt<'_>, + span: Span, + ident: Ident, + generics: &Generics, + trait_name: Symbol, + trait_args: Vec, + push: &mut dyn FnMut(Annotatable), +) { + let mut trait_parts = path!(span, core::marker); + trait_parts.push(Ident::new(trait_name, span)); + let trait_path = cx.path_all(span, true, trait_parts, trait_args); + let trait_ref = cx.trait_ref(trait_path); + + let self_params: Vec<_> = generics + .params + .iter() + .map(|param| match param.kind { + GenericParamKind::Lifetime => { + GenericArg::Lifetime(cx.lifetime(param.span(), param.ident)) + } + GenericParamKind::Type { .. } => { + GenericArg::Type(cx.ty_ident(param.span(), param.ident)) + } + GenericParamKind::Const { .. } => { + GenericArg::Const(cx.const_ident(param.span(), param.ident)) + } + }) + .collect(); + let self_ty = cx.ty_path(cx.path_all(span, false, vec![ident], self_params)); + + push(Annotatable::Item(cx.item( + span, + thin_vec::thin_vec![cx.attr_word(sym::automatically_derived, span)], + ast::ItemKind::Impl(ast::Impl { + generics: impl_generics(cx, generics), + of_trait: Some(Box::new(ast::TraitImplHeader { + safety: ast::Safety::Default, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Implicit, + trait_ref, + })), + constness: ast::Const::No, + self_ty, + items: ThinVec::new(), + }), + ))); +} + +fn impl_generics(cx: &ExtCtxt<'_>, generics: &Generics) -> Generics { + // Rebuild the generic parameter declarations because defaults are allowed on structs but + // rejected on impls. Preserve lifetime, type, and const parameters and their bounds, const + // parameter types, and the where-clause, while omitting type and const defaults. + Generics { + params: generics + .params + .iter() + .map(|param| match ¶m.kind { + GenericParamKind::Lifetime => { + cx.lifetime_param(param.span(), param.ident, param.bounds.clone()) + } + GenericParamKind::Type { default: _ } => { + cx.typaram(param.span(), param.ident, param.bounds.clone(), None) + } + GenericParamKind::Const { ty, span: _, default: _ } => cx.const_param( + param.span(), + param.ident, + param.bounds.clone(), + ty.clone(), + None, + ), + }) + .collect(), + where_clause: generics.where_clause.clone(), + span: generics.span, + } +} + +#[derive(Diagnostic)] +#[diag("`derive({$trait_name})` is only supported for structs, not {$kind}s", code = E0802)] +struct UnsupportedItem { + #[primary_span] + span: Span, + trait_name: Symbol, + kind: &'static str, +} + +#[derive(Diagnostic)] +#[diag("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute", code = E0802)] +struct MissingTarget { + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)] +struct DuplicateTarget { + #[primary_span] + duplicate: Span, + #[note("first `#[coerce_shared(Target)]` attribute is here")] + first: Span, +} + +#[derive(Diagnostic)] +#[diag("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)] +#[note("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")] +struct MalformedTarget { + #[primary_span] + span: Span, +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 78bf7d97bd7b8..8f8d8b3149440 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -130,6 +130,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register_derive! { Clone: clone::expand_deriving_clone, + CoerceShared: reborrow::expand_deriving_coerce_shared, Copy: bounds::expand_deriving_copy, ConstParamTy: bounds::expand_deriving_const_param_ty, Debug: debug::expand_deriving_debug, @@ -140,6 +141,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { PartialEq: partial_eq::expand_deriving_partial_eq, PartialOrd: partial_ord::expand_deriving_partial_ord, CoercePointee: coerce_pointee::expand_deriving_coerce_pointee, + Reborrow: reborrow::expand_deriving_reborrow, From: from::expand_deriving_from, } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index f9c21f42d4e5e..87fafc6db589d 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -101,6 +101,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(true) } } + // Types that are considered transparent in `unfold_transparent` should also act + // like transparent types here. + ty::Pat(base, _) => self.has_trivial_abi(self.layout_of(base)?), + ty::UnsafeBinder(bound_ty) => { + let ty = self.tcx.instantiate_bound_regions_with_erased(bound_ty.into()); + self.has_trivial_abi(self.layout_of(ty)?) + } ty::Alias(..) => panic!("non-normalized type"), _ => interp_ok(false), @@ -116,8 +123,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout: TyAndLayout<'tcx>, may_unfold: impl Fn(AdtDef<'tcx>) -> bool, ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - match layout.ty.kind() { - ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(*adt_def) => { + match *layout.ty.kind() { + ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(adt_def) => { assert_matches!(layout.variants, rustc_abi::Variants::Single { .. }); // Look for non-trivial-ABI field(s). let mut found = None; @@ -142,7 +149,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Recurse. self.unfold_transparent(field, may_unfold) } - ty::Pat(base, _) => interp_ok(self.layout_of(*base)?), + ty::Pat(base, _) => self.unfold_transparent(self.layout_of(base)?, may_unfold), + ty::UnsafeBinder(bound_ty) => { + let ty = self.tcx.instantiate_bound_regions_with_erased(bound_ty.into()); + self.unfold_transparent(self.layout_of(ty)?, may_unfold) + } // Not a transparent type, no further unfolding. _ => interp_ok(layout), } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 165cf7855b83b..07d6eb03ae8da 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -188,6 +188,7 @@ symbols! { Clone, CoercePointee, CoercePointeeValidated, + CoerceShared, CoerceUnsized, Const, ConstParamTy, 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 9071bf521394d..1897ed8fc84eb 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 @@ -1655,7 +1655,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut file = None; - let (msg, span, closure_span) = values + let (msg, mut span, mut closure_span) = values .and_then(|(predicate, normalized_term, expected_term)| { self.maybe_detailed_projection_msg( obligation.cause.span, @@ -1676,6 +1676,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None, ) }); + + // When the obligation comes from a closure arg and the projection isn't FnOnceOutput + // (which maybe_detailed_projection_msg handles via self_ty), point at the closure's + // return expression and label the closure declaration. + if closure_span.is_none() + && let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() + && let Node::Expr(arg_expr) = self.tcx.hir_node(*arg_hir_id) + && let hir::ExprKind::Closure(closure) = arg_expr.kind + && closure.kind == hir::ClosureKind::Closure + { + let body = self.tcx.hir_body(closure.body); + let ret_span = match body.value.kind { + hir::ExprKind::Block(hir::Block { expr: Some(expr), .. }, _) => expr.span, + hir::ExprKind::Block(hir::Block { expr: None, stmts: [.., last], .. }, _) => { + last.span + } + _ => body.value.span, + }; + if !closure.fn_decl_span.overlaps(ret_span) { + closure_span = Some(closure.fn_decl_span); + span = ret_span; + } + } + let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}"); *diag.long_ty_path() = file; let mut mention_bounds = true; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index e23f0bca9bb8d..e3d26218c09a0 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -1,4 +1,3 @@ -use core::cmp; use core::mem::{DropGuard, MaybeUninit}; use crate::io::{ @@ -827,9 +826,11 @@ where /// - avoid allocating unless necessary /// - avoid overallocating if we know the exact size (#89165) /// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) +/// - avoid re-initializing unfilled bytes into the spare buffer if we initialized >PROBE_SIZE unfilled bytes in a previous loop (#158008) /// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads -/// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems +/// - pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems /// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) +/// - also avoid <4 byte reads as this may split UTF-8 code points, which can be a problem for Windows console reads (#142847) #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_to_end( @@ -845,6 +846,9 @@ pub fn default_read_to_end( .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) .unwrap_or(DEFAULT_BUF_SIZE); + // Tracks how many bytes are initialized in the buffer + let mut init_until = buf.len(); + const PROBE_SIZE: usize = 32; fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { @@ -892,7 +896,7 @@ pub fn default_read_to_end( } loop { - if buf.len() == buf.capacity() && buf.capacity() == start_cap { + if buf.spare_capacity_mut().len() < PROBE_SIZE && buf.capacity() == start_cap { // The buffer might be an exact fit. Let's read into a probe buffer // and see if it returns `Ok(0)`. If so, we've avoided an // unnecessary doubling of the capacity. But if not, append the @@ -902,20 +906,42 @@ pub fn default_read_to_end( if read == 0 { return Ok(buf.len() - start_len); } + + init_until = buf.len(); + // In the case of very short reads, continue to use the stack buffer + // until either we reach the end or we need to reallocate. + continue; } - if buf.len() == buf.capacity() { - // buf is full, need more space + // Avoid unnecessarily short reads by ensuring there's at least PROBE_SIZE space available. + // And assert that PROBE_SIZE is always at least large enough to fit any UTF-8 encoded code point. + const { assert!(PROBE_SIZE >= char::MAX_LEN_UTF8) } + if buf.spare_capacity_mut().len() < PROBE_SIZE { buf.try_reserve(PROBE_SIZE)?; + // When reallocation occurs, we have to update init_until accordingly + // to re-calibrate how many bytes are actually initialized in the buffer + init_until = buf.len(); } + // We set a threshold of >PROBE_SIZE initialized yet unfilled bytes left in the + // spare buffer before determining that we need to initialize more bytes into + // the spare buffer + let buf_len = if init_until > buf.len() + PROBE_SIZE { + init_until - buf.len() + } else { + usize::min(max_read_size, buf.capacity() - buf.len()) + }; + let was_init = init_until >= buf.len() + buf_len; + let mut spare = buf.spare_capacity_mut(); - let buf_len = cmp::min(spare.len(), max_read_size); spare = &mut spare[..buf_len]; let mut read_buf: BorrowedBuf<'_, u8> = spare.into(); - // Note that we don't track already initialized bytes here, but this is fine - // because we explicitly limit the read size + if was_init { + // SAFETY: These bytes were initialized but not filled in the previous loop + unsafe { read_buf.set_init() }; + } + let mut cursor = read_buf.unfilled(); let result = loop { match r.read_buf(cursor.reborrow()) { @@ -929,6 +955,10 @@ pub fn default_read_to_end( let bytes_read = cursor.written(); let is_init = read_buf.is_init(); + if is_init { + init_until = buf.len() + buf_len; + } + // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. unsafe { let new_len = bytes_read + buf.len(); @@ -953,9 +983,11 @@ pub fn default_read_to_end( if !is_init { max_read_size = usize::MAX; } - // we have passed a larger buffer than previously and the - // reader still hasn't returned a short read - else if buf_len >= max_read_size && bytes_read == buf_len { + // the spare buffer has initialized and read in `max_read_size` bytes. + // it's possible that we have more than `max_read_size` bytes to read + // left, so a larger buffer may be necessary to minimize the number of + // iterations of reading in bytes to the buffer + else if bytes_read == max_read_size { max_read_size = max_read_size.saturating_mul(2); } } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index a1a1ec56d14a1..1eea7adf1a6d5 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1373,6 +1373,14 @@ pub trait Reborrow { /* compiler built-in */ } +/// Derive macro generating an impl of the trait `Reborrow`. +#[rustc_builtin_macro(Reborrow)] +#[allow_internal_unstable(reborrow)] +#[unstable(feature = "reborrow", issue = "145612")] +pub macro Reborrow($item:item) { + /* compiler built-in */ +} + /// Allows reborrowable value to be reborrowed as shared, creating a copy /// that disables the source for writes for the lifetime of the copy. #[lang = "coerce_shared"] @@ -1380,3 +1388,13 @@ pub trait Reborrow { pub trait CoerceShared: Reborrow { /* compiler built-in */ } + +/// Derive macro generating an impl of the trait `CoerceShared`. +/// +/// The shared target type must be specified with `#[coerce_shared(Target)]`. +#[rustc_builtin_macro(CoerceShared, attributes(coerce_shared))] +#[allow_internal_unstable(reborrow)] +#[unstable(feature = "reborrow", issue = "145612")] +pub macro CoerceShared($item:item) { + /* compiler built-in */ +} diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 1310255de3ca4..171ef1811ca40 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -1,272 +1,8 @@ -//! bootstrap, the Rust build system +//! The `main.rs` for bootstrap is a small stub that delegates to the real +//! entry point within the bootstrap library crate. //! -//! This is the entry point for the build system used to compile the `rustc` -//! compiler. Lots of documentation can be found in the `README.md` file in the -//! parent directory, and otherwise documentation can be found throughout the `build` -//! directory in each respective module. - -use std::fs::{self, OpenOptions, TryLockError}; -use std::io::{self, BufRead, BufReader, IsTerminal, Read, Write}; -use std::path::Path; -use std::str::FromStr; -use std::sync::Once; -use std::time::Instant; -use std::{env, process}; - -use bootstrap::{ - Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, StepStack, Subcommand, debug, - find_recent_config_change_ids, human_readable_changes, t, -}; - -fn is_tracing_enabled() -> bool { - cfg!(feature = "tracing") -} +//! Don't add more code here! Add it to the inner main instead. fn main() { - #[cfg(feature = "tracing")] - let guard = bootstrap::setup_tracing("BOOTSTRAP_TRACING"); - - let _start_time = Instant::now(); - - let default_panic_hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(move |info| { - static BACKTRACE_LOCK: Once = Once::new(); - - // Always print backtraces to provide richer errors, to help debug hard-to-reproduce panics - // when the user didn't specify RUST_BACKTRACE - // Note that we only override this variable in the panic handler, because bootstrap might - // manually capture backtraces when a command is executed, and in that case we do not want - // to always force backtraces. - BACKTRACE_LOCK.call_once(|| { - if std::env::var("RUST_BACKTRACE").is_err() { - unsafe { - std::env::set_var("RUST_BACKTRACE", "1"); - } - } - }); - - default_panic_hook(info); - StepStack::with_current(|stack| { - eprintln!("\nBootstrap has panicked, currently active steps:"); - for step in stack.get_active_steps() { - eprintln!("{} at {}", step.info, step.location); - } - }); - })); - - let args = env::args().skip(1).collect::>(); - - if Flags::try_parse_verbose_help(&args) { - return; - } - - debug!("parsing flags"); - let flags = Flags::parse(&args); - debug!("parsing config based on flags"); - let config = Config::parse(flags); - - let mut build_lock; - - if !config.bypass_bootstrap_lock { - // Display PID of process holding the lock - // PID will be stored in a lock file - let lock_path = config.out.join("lock"); - build_lock = t!(fs::OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(false) - .open(&lock_path)); - t!(build_lock.try_lock().or_else(|e| { - if let TryLockError::Error(e) = e { - return Err(e); - } - let mut pid = String::new(); - t!(build_lock.read_to_string(&mut pid)); - // #135972: We can reach this point when the lock has been taken, - // but the locker has not yet written its PID to the file - if !pid.is_empty() { - println!("WARNING: build directory locked by process {pid}, waiting for lock"); - } else { - println!("WARNING: build directory locked, waiting for lock"); - } - build_lock.lock() - })); - t!(build_lock.set_len(0)); - t!(build_lock.write_all(process::id().to_string().as_bytes())); - } - - // check_version warnings are not printed during setup, or during CI - let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) - || config.is_running_on_ci() - || config.dry_run() - { - None - } else { - check_version(&config) - }; - - // NOTE: Since `./configure` generates a `bootstrap.toml`, distro maintainers will see the - // changelog warning, not the `x.py setup` message. - let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. }); - if suggest_setup { - println!("WARNING: you have not made a `bootstrap.toml`"); - println!( - "HELP: consider running `./x.py setup` or copying `bootstrap.example.toml` by running \ - `cp bootstrap.example.toml bootstrap.toml`" - ); - } else if let Some(suggestion) = &changelog_suggestion { - println!("{suggestion}"); - } - - let pre_commit = config.src.join(".git").join("hooks").join("pre-commit"); - let dump_bootstrap_shims = config.dump_bootstrap_shims; - let out_dir = config.out.clone(); - - let tracing_enabled = is_tracing_enabled(); - - // Prepare a directory for tracing output - // Also store a symlink named "latest" to point to the latest tracing directory. - let tracing_dir = out_dir.join("bootstrap-trace").join(std::process::id().to_string()); - let latest_trace_dir = tracing_dir.parent().unwrap().join("latest"); - if tracing_enabled { - let _ = std::fs::remove_dir_all(&tracing_dir); - std::fs::create_dir_all(&tracing_dir).unwrap(); - - #[cfg(windows)] - let _ = std::fs::remove_dir(&latest_trace_dir); - #[cfg(not(windows))] - let _ = std::fs::remove_file(&latest_trace_dir); - - #[cfg(not(windows))] - fn symlink_dir_inner(original: &Path, link: &Path) -> io::Result<()> { - use std::os::unix::fs; - fs::symlink(original, link) - } - - #[cfg(windows)] - fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> { - junction::create(target, junction) - } - - t!(symlink_dir_inner(&tracing_dir, &latest_trace_dir)); - } - - debug!("creating new build based on config"); - let mut build = Build::new(config); - build.build(); - - if suggest_setup { - println!("WARNING: you have not made a `bootstrap.toml`"); - println!( - "HELP: consider running `./x.py setup` or copying `bootstrap.example.toml` by running \ - `cp bootstrap.example.toml bootstrap.toml`" - ); - } else if let Some(suggestion) = &changelog_suggestion { - println!("{suggestion}"); - } - - // Give a warning if the pre-commit script is in pre-commit and not pre-push. - // HACK: Since the commit script uses hard links, we can't actually tell if it was installed by x.py setup or not. - // We could see if it's identical to src/etc/pre-push.sh, but pre-push may have been modified in the meantime. - // Instead, look for this comment, which is almost certainly not in any custom hook. - if fs::read_to_string(pre_commit).is_ok_and(|contents| { - contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570") - }) { - println!( - "WARNING: You have the pre-push script installed to .git/hooks/pre-commit. \ - Consider moving it to .git/hooks/pre-push instead, which runs less often." - ); - } - - if suggest_setup || changelog_suggestion.is_some() { - println!("NOTE: this message was printed twice to make it more likely to be seen"); - } - - if dump_bootstrap_shims { - let dump_dir = out_dir.join("bootstrap-shims-dump"); - assert!(dump_dir.exists()); - - for entry in walkdir::WalkDir::new(&dump_dir) { - let entry = t!(entry); - - if !entry.file_type().is_file() { - continue; - } - - let file = t!(fs::File::open(entry.path())); - - // To ensure deterministic results we must sort the dump lines. - // This is necessary because the order of rustc invocations different - // almost all the time. - let mut lines: Vec = t!(BufReader::new(&file).lines().collect()); - lines.sort_by_key(|t| t.to_lowercase()); - let mut file = t!(OpenOptions::new().write(true).truncate(true).open(entry.path())); - t!(file.write_all(lines.join("\n").as_bytes())); - } - } - - #[cfg(feature = "tracing")] - { - build.report_summary(&tracing_dir.join("command-stats.txt"), _start_time); - build.report_step_graph(&tracing_dir); - guard.copy_to_dir(&tracing_dir); - eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); - } -} - -fn check_version(config: &Config) -> Option { - let mut msg = String::new(); - - let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id; - let warned_id_path = config.out.join("bootstrap").join(".last-warned-change-id"); - - let mut id = match config.change_id { - Some(ChangeId::Id(id)) if id == latest_change_id => return None, - Some(ChangeId::Ignore) => return None, - Some(ChangeId::Id(id)) => id, - None => { - msg.push_str("WARNING: The `change-id` is missing in the `bootstrap.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n"); - msg.push_str("NOTE: to silence this warning, "); - msg.push_str(&format!( - "add `change-id = {latest_change_id}` or `change-id = \"ignore\"` at the top of `bootstrap.toml`" - )); - return Some(msg); - } - }; - - // Always try to use `change-id` from .last-warned-change-id first. If it doesn't exist, - // then use the one from the bootstrap.toml. This way we never show the same warnings - // more than once. - if let Ok(t) = fs::read_to_string(&warned_id_path) { - let last_warned_id = usize::from_str(&t) - .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display())); - - // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`. - // Otherwise, we may retrieve all the changes if it's not the highest value. - // For better understanding, refer to `change_tracker::find_recent_config_change_ids`. - if CONFIG_CHANGE_HISTORY.iter().any(|config| config.change_id == last_warned_id) { - id = last_warned_id; - } - }; - - let changes = find_recent_config_change_ids(id); - - if changes.is_empty() { - return None; - } - - msg.push_str("There have been changes to x.py since you last updated:\n"); - msg.push_str(&human_readable_changes(changes)); - - msg.push_str("NOTE: to silence this warning, "); - msg.push_str(&format!( - "update `bootstrap.toml` to use `change-id = {latest_change_id}` or `change-id = \"ignore\"` instead" - )); - - if io::stdout().is_terminal() { - t!(fs::write(warned_id_path, latest_change_id.to_string())); - } - - Some(msg) + bootstrap::cli_main::main(); } diff --git a/src/bootstrap/src/cli_main.rs b/src/bootstrap/src/cli_main.rs new file mode 100644 index 0000000000000..879cf7159fb6b --- /dev/null +++ b/src/bootstrap/src/cli_main.rs @@ -0,0 +1,272 @@ +//! bootstrap, the Rust build system +//! +//! This is the entry point for the build system used to compile the `rustc` +//! compiler. Lots of documentation can be found in the `README.md` file in the +//! parent directory, and otherwise documentation can be found throughout the `build` +//! directory in each respective module. + +use std::fs::{self, OpenOptions, TryLockError}; +use std::io::{self, BufRead, BufReader, IsTerminal, Read, Write}; +use std::path::Path; +use std::str::FromStr; +use std::sync::Once; +use std::time::Instant; +use std::{env, process}; + +use crate::{ + Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, StepStack, Subcommand, debug, + find_recent_config_change_ids, human_readable_changes, t, +}; + +fn is_tracing_enabled() -> bool { + cfg!(feature = "tracing") +} + +pub fn main() { + #[cfg(feature = "tracing")] + let guard = crate::utils::tracing::setup_tracing("BOOTSTRAP_TRACING"); + + let _start_time = Instant::now(); + + let default_panic_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + static BACKTRACE_LOCK: Once = Once::new(); + + // Always print backtraces to provide richer errors, to help debug hard-to-reproduce panics + // when the user didn't specify RUST_BACKTRACE + // Note that we only override this variable in the panic handler, because bootstrap might + // manually capture backtraces when a command is executed, and in that case we do not want + // to always force backtraces. + BACKTRACE_LOCK.call_once(|| { + if std::env::var("RUST_BACKTRACE").is_err() { + unsafe { + std::env::set_var("RUST_BACKTRACE", "1"); + } + } + }); + + default_panic_hook(info); + StepStack::with_current(|stack| { + eprintln!("\nBootstrap has panicked, currently active steps:"); + for step in stack.get_active_steps() { + eprintln!("{} at {}", step.info, step.location); + } + }); + })); + + let args = env::args().skip(1).collect::>(); + + if Flags::try_parse_verbose_help(&args) { + return; + } + + debug!("parsing flags"); + let flags = Flags::parse(&args); + debug!("parsing config based on flags"); + let config = Config::parse(flags); + + let mut build_lock; + + if !config.bypass_bootstrap_lock { + // Display PID of process holding the lock + // PID will be stored in a lock file + let lock_path = config.out.join("lock"); + build_lock = t!(fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(false) + .open(&lock_path)); + t!(build_lock.try_lock().or_else(|e| { + if let TryLockError::Error(e) = e { + return Err(e); + } + let mut pid = String::new(); + t!(build_lock.read_to_string(&mut pid)); + // #135972: We can reach this point when the lock has been taken, + // but the locker has not yet written its PID to the file + if !pid.is_empty() { + println!("WARNING: build directory locked by process {pid}, waiting for lock"); + } else { + println!("WARNING: build directory locked, waiting for lock"); + } + build_lock.lock() + })); + t!(build_lock.set_len(0)); + t!(build_lock.write_all(process::id().to_string().as_bytes())); + } + + // check_version warnings are not printed during setup, or during CI + let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) + || config.is_running_on_ci() + || config.dry_run() + { + None + } else { + check_version(&config) + }; + + // NOTE: Since `./configure` generates a `bootstrap.toml`, distro maintainers will see the + // changelog warning, not the `x.py setup` message. + let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. }); + if suggest_setup { + println!("WARNING: you have not made a `bootstrap.toml`"); + println!( + "HELP: consider running `./x.py setup` or copying `bootstrap.example.toml` by running \ + `cp bootstrap.example.toml bootstrap.toml`" + ); + } else if let Some(suggestion) = &changelog_suggestion { + println!("{suggestion}"); + } + + let pre_commit = config.src.join(".git").join("hooks").join("pre-commit"); + let dump_bootstrap_shims = config.dump_bootstrap_shims; + let out_dir = config.out.clone(); + + let tracing_enabled = is_tracing_enabled(); + + // Prepare a directory for tracing output + // Also store a symlink named "latest" to point to the latest tracing directory. + let tracing_dir = out_dir.join("bootstrap-trace").join(std::process::id().to_string()); + let latest_trace_dir = tracing_dir.parent().unwrap().join("latest"); + if tracing_enabled { + let _ = std::fs::remove_dir_all(&tracing_dir); + std::fs::create_dir_all(&tracing_dir).unwrap(); + + #[cfg(windows)] + let _ = std::fs::remove_dir(&latest_trace_dir); + #[cfg(not(windows))] + let _ = std::fs::remove_file(&latest_trace_dir); + + #[cfg(not(windows))] + fn symlink_dir_inner(original: &Path, link: &Path) -> io::Result<()> { + use std::os::unix::fs; + fs::symlink(original, link) + } + + #[cfg(windows)] + fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> { + junction::create(target, junction) + } + + t!(symlink_dir_inner(&tracing_dir, &latest_trace_dir)); + } + + debug!("creating new build based on config"); + let mut build = Build::new(config); + build.build(); + + if suggest_setup { + println!("WARNING: you have not made a `bootstrap.toml`"); + println!( + "HELP: consider running `./x.py setup` or copying `bootstrap.example.toml` by running \ + `cp bootstrap.example.toml bootstrap.toml`" + ); + } else if let Some(suggestion) = &changelog_suggestion { + println!("{suggestion}"); + } + + // Give a warning if the pre-commit script is in pre-commit and not pre-push. + // HACK: Since the commit script uses hard links, we can't actually tell if it was installed by x.py setup or not. + // We could see if it's identical to src/etc/pre-push.sh, but pre-push may have been modified in the meantime. + // Instead, look for this comment, which is almost certainly not in any custom hook. + if fs::read_to_string(pre_commit).is_ok_and(|contents| { + contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570") + }) { + println!( + "WARNING: You have the pre-push script installed to .git/hooks/pre-commit. \ + Consider moving it to .git/hooks/pre-push instead, which runs less often." + ); + } + + if suggest_setup || changelog_suggestion.is_some() { + println!("NOTE: this message was printed twice to make it more likely to be seen"); + } + + if dump_bootstrap_shims { + let dump_dir = out_dir.join("bootstrap-shims-dump"); + assert!(dump_dir.exists()); + + for entry in walkdir::WalkDir::new(&dump_dir) { + let entry = t!(entry); + + if !entry.file_type().is_file() { + continue; + } + + let file = t!(fs::File::open(entry.path())); + + // To ensure deterministic results we must sort the dump lines. + // This is necessary because the order of rustc invocations different + // almost all the time. + let mut lines: Vec = t!(BufReader::new(&file).lines().collect()); + lines.sort_by_key(|t| t.to_lowercase()); + let mut file = t!(OpenOptions::new().write(true).truncate(true).open(entry.path())); + t!(file.write_all(lines.join("\n").as_bytes())); + } + } + + #[cfg(feature = "tracing")] + { + build.report_summary(&tracing_dir.join("command-stats.txt"), _start_time); + build.report_step_graph(&tracing_dir); + guard.copy_to_dir(&tracing_dir); + eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); + } +} + +fn check_version(config: &Config) -> Option { + let mut msg = String::new(); + + let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id; + let warned_id_path = config.out.join("bootstrap").join(".last-warned-change-id"); + + let mut id = match config.change_id { + Some(ChangeId::Id(id)) if id == latest_change_id => return None, + Some(ChangeId::Ignore) => return None, + Some(ChangeId::Id(id)) => id, + None => { + msg.push_str("WARNING: The `change-id` is missing in the `bootstrap.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n"); + msg.push_str("NOTE: to silence this warning, "); + msg.push_str(&format!( + "add `change-id = {latest_change_id}` or `change-id = \"ignore\"` at the top of `bootstrap.toml`" + )); + return Some(msg); + } + }; + + // Always try to use `change-id` from .last-warned-change-id first. If it doesn't exist, + // then use the one from the bootstrap.toml. This way we never show the same warnings + // more than once. + if let Ok(t) = fs::read_to_string(&warned_id_path) { + let last_warned_id = usize::from_str(&t) + .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display())); + + // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`. + // Otherwise, we may retrieve all the changes if it's not the highest value. + // For better understanding, refer to `change_tracker::find_recent_config_change_ids`. + if CONFIG_CHANGE_HISTORY.iter().any(|config| config.change_id == last_warned_id) { + id = last_warned_id; + } + }; + + let changes = find_recent_config_change_ids(id); + + if changes.is_empty() { + return None; + } + + msg.push_str("There have been changes to x.py since you last updated:\n"); + msg.push_str(&human_readable_changes(changes)); + + msg.push_str("NOTE: to silence this warning, "); + msg.push_str(&format!( + "update `bootstrap.toml` to use `change-id = {latest_change_id}` or `change-id = \"ignore\"` instead" + )); + + if io::stdout().is_terminal() { + t!(fs::write(warned_id_path, latest_change_id.to_string())); + } + + Some(msg) +} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index a276f0378f450..f9b29a155eab1 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -28,36 +28,31 @@ use std::{env, fs, io, str}; use build_helper::ci::gha; use cc::Tool; use termcolor::{ColorChoice, StandardStream, WriteColor}; -use utils::build_stamp::BuildStamp; -use utils::channel::GitInfo; -use utils::exec::ExecutionContext; - -use crate::core::build_steps::format::InternalRustfmt; -use crate::core::builder; -use crate::core::builder::Kind; -use crate::core::config::{BootstrapOverrideLld, DryRun, LlvmLibunwind, TargetSelection, flags}; -use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo}; - -mod core; -mod utils; - #[cfg(feature = "tracing")] -pub use core::builder::STEP_SPAN_TARGET; -pub use core::builder::{PathSet, StepStack}; -pub use core::config::flags::{Flags, Subcommand}; -pub use core::config::{ChangeId, Config}; +use tracing::{instrument, span}; +use crate::core::build_steps::format::InternalRustfmt; +use crate::core::build_steps::vendor::VENDOR_DIR; #[cfg(feature = "tracing")] -use tracing::{instrument, span}; -pub use utils::change_tracker::{ +use crate::core::builder::STEP_SPAN_TARGET; +use crate::core::builder::{self, Kind, StepStack}; +use crate::core::config::flags::{Flags, Subcommand}; +use crate::core::config::{ + BootstrapOverrideLld, ChangeId, Config, DryRun, LlvmLibunwind, TargetSelection, flags, +}; +use crate::utils::build_stamp::BuildStamp; +use crate::utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; -pub use utils::helpers::{PanicTracker, symlink_dir}; -#[cfg(feature = "tracing")] -pub use utils::tracing::setup_tracing; +use crate::utils::channel::GitInfo; +use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; +use crate::utils::helpers::{ + self, PanicTracker, dir_is_empty, exe, libdir, set_file_times, split_debuginfo, symlink_dir, +}; -use crate::core::build_steps::vendor::VENDOR_DIR; +pub mod cli_main; +mod core; +mod utils; const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report diff --git a/tests/ui/closures/closure-arg-assoc-type-mismatch.rs b/tests/ui/closures/closure-arg-assoc-type-mismatch.rs new file mode 100644 index 0000000000000..6681b6794543e --- /dev/null +++ b/tests/ui/closures/closure-arg-assoc-type-mismatch.rs @@ -0,0 +1,30 @@ +// When a closure is passed as a function argument and the E0271 projection +// mismatch is about an associated type of the closure's return type, the error +// should point at the return expression inside the closure and label the +// closure declaration — mirroring the FnOnce-output diagnostic. +// https://github.com/rust-lang/rust/issues/42390 + +trait MyTrait { + type Item; +} + +struct S; +impl MyTrait for S { + type Item = u32; +} + +// Bound on the closure's output type's associated type. +fn needs_unit_item S>(_f: F) +where + F::Output: MyTrait, +{ +} + +fn main() { + needs_unit_item(|| S); //~ ERROR type mismatch resolving `::Item == ()` + + needs_unit_item(|| { S }); //~ ERROR type mismatch resolving `::Item == ()` + + let x = S; + needs_unit_item(|| { let _ = 0; x }); //~ ERROR type mismatch resolving `::Item == ()` +} diff --git a/tests/ui/closures/closure-arg-assoc-type-mismatch.stderr b/tests/ui/closures/closure-arg-assoc-type-mismatch.stderr new file mode 100644 index 0000000000000..09ae41b30945b --- /dev/null +++ b/tests/ui/closures/closure-arg-assoc-type-mismatch.stderr @@ -0,0 +1,72 @@ +error[E0271]: type mismatch resolving `::Item == ()` + --> $DIR/closure-arg-assoc-type-mismatch.rs:24:24 + | +LL | needs_unit_item(|| S); + | --------------- -- ^ type mismatch resolving `::Item == ()` + | | | + | | this closure + | required by a bound introduced by this call + | +note: expected this to be `()` + --> $DIR/closure-arg-assoc-type-mismatch.rs:13:17 + | +LL | type Item = u32; + | ^^^ +note: required by a bound in `needs_unit_item` + --> $DIR/closure-arg-assoc-type-mismatch.rs:19:24 + | +LL | fn needs_unit_item S>(_f: F) + | --------------- required by a bound in this function +LL | where +LL | F::Output: MyTrait, + | ^^^^^^^^^ required by this bound in `needs_unit_item` + +error[E0271]: type mismatch resolving `::Item == ()` + --> $DIR/closure-arg-assoc-type-mismatch.rs:26:26 + | +LL | needs_unit_item(|| { S }); + | --------------- -- ^ type mismatch resolving `::Item == ()` + | | | + | | this closure + | required by a bound introduced by this call + | +note: expected this to be `()` + --> $DIR/closure-arg-assoc-type-mismatch.rs:13:17 + | +LL | type Item = u32; + | ^^^ +note: required by a bound in `needs_unit_item` + --> $DIR/closure-arg-assoc-type-mismatch.rs:19:24 + | +LL | fn needs_unit_item S>(_f: F) + | --------------- required by a bound in this function +LL | where +LL | F::Output: MyTrait, + | ^^^^^^^^^ required by this bound in `needs_unit_item` + +error[E0271]: type mismatch resolving `::Item == ()` + --> $DIR/closure-arg-assoc-type-mismatch.rs:29:37 + | +LL | needs_unit_item(|| { let _ = 0; x }); + | --------------- -- ^ type mismatch resolving `::Item == ()` + | | | + | | this closure + | required by a bound introduced by this call + | +note: expected this to be `()` + --> $DIR/closure-arg-assoc-type-mismatch.rs:13:17 + | +LL | type Item = u32; + | ^^^ +note: required by a bound in `needs_unit_item` + --> $DIR/closure-arg-assoc-type-mismatch.rs:19:24 + | +LL | fn needs_unit_item S>(_f: F) + | --------------- required by a bound in this function +LL | where +LL | F::Output: MyTrait, + | ^^^^^^^^^ required by this bound in `needs_unit_item` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs index 48a14959d8d64..a79669e817008 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs @@ -1,3 +1,13 @@ -use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` +use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` +//~^ ERROR use of unstable library feature `reborrow` + +#[derive(Clone, Copy)] +struct CustomRef<'a>(&'a ()); + +#[derive(std::marker::Reborrow, std::marker::CoerceShared)] +//~^ ERROR use of unstable library feature `reborrow` +//~| ERROR use of unstable library feature `reborrow` +#[coerce_shared(CustomRef<'a>)] +struct CustomMut<'a>(&'a mut ()); fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr index c4c5e06778af3..36d6a627221fd 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr @@ -1,3 +1,33 @@ +error[E0658]: use of unstable library feature `reborrow` + --> $DIR/feature-gate-reborrow-coerce-shared.rs:7:10 + | +LL | #[derive(std::marker::Reborrow, std::marker::CoerceShared)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #145612 for more information + = help: add `#![feature(reborrow)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `reborrow` + --> $DIR/feature-gate-reborrow-coerce-shared.rs:7:33 + | +LL | #[derive(std::marker::Reborrow, std::marker::CoerceShared)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #145612 for more information + = help: add `#![feature(reborrow)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `reborrow` + --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 + | +LL | use std::marker::CoerceShared; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #145612 for more information + = help: add `#![feature(reborrow)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 | @@ -7,7 +37,8 @@ LL | use std::marker::CoerceShared; = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 1 previous error +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-reborrow.rs b/tests/ui/feature-gates/feature-gate-reborrow.rs index f016f6c6bfa59..1b0bfafeae99c 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow.rs @@ -1,3 +1,7 @@ -use std::marker::Reborrow; //~ ERROR use of unstable library feature `reborrow` +use std::marker::Reborrow; //~ ERROR use of unstable library feature `reborrow` +//~^ ERROR use of unstable library feature `reborrow` + +#[derive(std::marker::Reborrow)] //~ ERROR use of unstable library feature `reborrow` +struct CustomMut<'a>(&'a mut ()); fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow.stderr b/tests/ui/feature-gates/feature-gate-reborrow.stderr index 5e3033f3bf1fe..a5102ac0813ed 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow.stderr @@ -1,3 +1,23 @@ +error[E0658]: use of unstable library feature `reborrow` + --> $DIR/feature-gate-reborrow.rs:4:10 + | +LL | #[derive(std::marker::Reborrow)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #145612 for more information + = help: add `#![feature(reborrow)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `reborrow` + --> $DIR/feature-gate-reborrow.rs:1:5 + | +LL | use std::marker::Reborrow; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #145612 for more information + = help: add `#![feature(reborrow)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow.rs:1:5 | @@ -7,7 +27,8 @@ LL | use std::marker::Reborrow; = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 1 previous error +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index eab9bf1ae34a8..2c51d1e9aec4d 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -13,11 +13,12 @@ note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0271]: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:29 + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:38 | LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^^ types differ - | | + | ---- -------- ^^^^^^^^^^^^^^^ types differ + | | | + | | this closure | required by a bound introduced by this call | note: required by a bound in `find` @@ -38,11 +39,12 @@ note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0271]: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:10:29 + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:10:41 | LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ - | | + | ---- ----------- ^^^^^^^^^^^^^^^ types differ + | | | + | | this closure | required by a bound introduced by this call | note: required by a bound in `find` diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs deleted file mode 100644 index 80689d81d0cc1..0000000000000 --- a/tests/ui/reborrow/custom_marker.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ run-pass - -#![feature(reborrow)] -use std::marker::{Reborrow, PhantomData}; - -struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} - -fn method<'a>(_a: CustomMarker<'a>) -> &'a () { - &() -} - -fn main() { - let a = CustomMarker(PhantomData); - let _ = method(a); - let _ = method(a); -} diff --git a/tests/ui/reborrow/custom_marker_assign_deref.rs b/tests/ui/reborrow/custom_marker_assign_deref.rs index 79ea2a35acdaf..9c0501644f83b 100644 --- a/tests/ui/reborrow/custom_marker_assign_deref.rs +++ b/tests/ui/reborrow/custom_marker_assign_deref.rs @@ -3,8 +3,8 @@ #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; +#[derive(Reborrow)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} impl<'a> std::ops::Deref for CustomMarker<'a> { type Target = CustomMarker<'a>; diff --git a/tests/ui/reborrow/custom_marker_coerce_shared.rs b/tests/ui/reborrow/custom_marker_coerce_shared.rs deleted file mode 100644 index 17c7bac98d17a..0000000000000 --- a/tests/ui/reborrow/custom_marker_coerce_shared.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass - -#![feature(reborrow)] -use std::marker::{CoerceShared, PhantomData, Reborrow}; - -struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} -#[derive(Debug, Clone, Copy)] -struct CustomMarkerRef<'a>(PhantomData<&'a ()>); -impl<'a> CoerceShared> for CustomMarker<'a> {} - - -fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { - &() -} - -fn main() { - let a = CustomMarker(PhantomData); - let b = method(a); - let c = method(a); - let _ = (b, c); -} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs index 56bc1f896da0f..b84b63234b8c2 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -3,12 +3,11 @@ #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(CustomMarkerRef<'a>)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} #[derive(Debug, Clone, Copy)] struct CustomMarkerRef<'a>(PhantomData<&'a ()>); -impl<'a> CoerceShared> for CustomMarker<'a> {} - fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs index 532d13da258c8..027e45bd5ba49 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -1,12 +1,11 @@ #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(CustomMarkerRef<'a>)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} #[derive(Clone, Copy)] struct CustomMarkerRef<'a>(PhantomData<&'a ()>); -impl<'a> CoerceShared> for CustomMarker<'a> {} - fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr index 90382af3ce30e..0089c03e36c77 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/custom_marker_coerce_shared_move.rs:19:14 + --> $DIR/custom_marker_coerce_shared_move.rs:18:14 | LL | let a = CustomMarker(PhantomData); | - binding `a` declared here diff --git a/tests/ui/reborrow/custom_marker_deref.rs b/tests/ui/reborrow/custom_marker_deref.rs index 74b9bac22ed0e..3dcf26d6829d1 100644 --- a/tests/ui/reborrow/custom_marker_deref.rs +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -3,8 +3,8 @@ #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; +#[derive(Reborrow)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.rs b/tests/ui/reborrow/custom_marker_mut_a_b.rs index 3baf320b583b7..ebed96096b206 100644 --- a/tests/ui/reborrow/custom_marker_mut_a_b.rs +++ b/tests/ui/reborrow/custom_marker_mut_a_b.rs @@ -1,8 +1,8 @@ #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; +#[derive(Reborrow)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_mut_self.rs b/tests/ui/reborrow/custom_marker_mut_self.rs index a688f503517d0..c4c4485f81671 100644 --- a/tests/ui/reborrow/custom_marker_mut_self.rs +++ b/tests/ui/reborrow/custom_marker_mut_self.rs @@ -1,8 +1,8 @@ #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; +#[derive(Reborrow)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.rs b/tests/ui/reborrow/custom_marker_mut_self_a.rs index f4cc8defb05e6..64cc107eef829 100644 --- a/tests/ui/reborrow/custom_marker_mut_self_a.rs +++ b/tests/ui/reborrow/custom_marker_mut_self_a.rs @@ -1,8 +1,8 @@ #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; +#[derive(Reborrow)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.rs b/tests/ui/reborrow/custom_marker_mut_self_b.rs index 16356954908b0..3a4baeb241677 100644 --- a/tests/ui/reborrow/custom_marker_mut_self_b.rs +++ b/tests/ui/reborrow/custom_marker_mut_self_b.rs @@ -1,8 +1,8 @@ #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; +#[derive(Reborrow)] struct CustomMarker<'a>(PhantomData<&'a ()>); -impl<'a> Reborrow for CustomMarker<'a> {} fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.rs b/tests/ui/reborrow/custom_marker_two_lifetimes.rs deleted file mode 100644 index d03282145054d..0000000000000 --- a/tests/ui/reborrow/custom_marker_two_lifetimes.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(reborrow)] -use std::marker::{Reborrow, PhantomData}; - -struct CustomMarker<'a, 'b>(PhantomData<&'a mut ()>, PhantomData<&'b ()>); -impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} -//~^ ERROR: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target - -fn main() {} diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.stderr b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr deleted file mode 100644 index ce5c4d09aeb79..0000000000000 --- a/tests/ui/reborrow/custom_marker_two_lifetimes.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target - --> $DIR/custom_marker_two_lifetimes.rs:5:1 - | -LL | impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/derive_coerce_shared.rs b/tests/ui/reborrow/derive_coerce_shared.rs new file mode 100644 index 0000000000000..4a4e2d609deee --- /dev/null +++ b/tests/ui/reborrow/derive_coerce_shared.rs @@ -0,0 +1,87 @@ +//@ run-pass + +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomRef<'a, T> { + value: &'a T, +} +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(CustomRef<'a, T>)] +struct CustomMut<'a, T> { + value: &'a mut T, +} + +struct TupleRef<'a, T>(&'a T); +impl<'a, T> Clone for TupleRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for TupleRef<'a, T> {} + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(TupleRef<'a, T>)] +struct TupleMut<'a, T>(&'a mut T); + +struct ArrayRef<'a, T, const N: usize>(&'a [T; N]); +impl<'a, T, const N: usize> Clone for ArrayRef<'a, T, N> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T, const N: usize> Copy for ArrayRef<'a, T, N> {} + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(ArrayRef<'a, T, N>)] +struct ArrayMut<'a, T, const N: usize>(&'a mut [T; N]); + +#[derive(Clone, Copy)] +struct MarkerRef<'a>(PhantomData<&'a ()>); + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(MarkerRef<'a>)] +struct MarkerMut<'a>(PhantomData<&'a ()>); + +#[derive(Clone, Copy)] +struct SharedOnlyRef<'a>(&'a ()); + +#[derive(CoerceShared)] +#[coerce_shared(SharedOnlyRef<'a>)] +struct SharedOnlyMut<'a>(&'a mut ()); +impl<'a> Reborrow for SharedOnlyMut<'a> {} + +fn take_custom(_: CustomRef<'_, ()>) {} +fn take_tuple(_: TupleRef<'_, ()>) {} +fn take_array(_: ArrayRef<'_, (), 1>) {} +fn take_marker<'a>(_: MarkerRef<'a>) -> &'a () { + &() +} +fn take_shared_only(_: SharedOnlyRef<'_>) {} + +fn main() { + let custom = CustomMut { value: &mut () }; + take_custom(custom); + + let tuple = TupleMut(&mut ()); + take_tuple(tuple); + + let array = ArrayMut(&mut [()]); + take_array(array); + + let marker = MarkerMut(PhantomData); + let _ = take_marker(marker); + let _ = take_marker(marker); + + let shared_only = SharedOnlyMut(&mut ()); + take_shared_only(shared_only); +} diff --git a/tests/ui/reborrow/derive_coerce_shared_attr.rs b/tests/ui/reborrow/derive_coerce_shared_attr.rs new file mode 100644 index 0000000000000..dc9209fe4ba9c --- /dev/null +++ b/tests/ui/reborrow/derive_coerce_shared_attr.rs @@ -0,0 +1,33 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +#[derive(Reborrow, CoerceShared)] +//~^ ERROR `derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute +struct MissingTarget<'a>(&'a mut ()); + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(Target<'a>)] +#[coerce_shared(Target<'a>)] +//~^ ERROR duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` +struct DuplicateTarget<'a>(&'a mut ()); + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared] +//~^ ERROR malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` +struct MalformedTargetWord<'a>(&'a mut ()); + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared()] +//~^ ERROR malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` +struct MalformedTargetEmpty<'a>(&'a mut ()); + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(Target<'a>, Target<'a>)] +//~^ ERROR malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` +struct MalformedTargetList<'a>(&'a mut ()); + +#[derive(Clone, Copy)] +struct Target<'a>(&'a ()); + +fn main() {} diff --git a/tests/ui/reborrow/derive_coerce_shared_attr.stderr b/tests/ui/reborrow/derive_coerce_shared_attr.stderr new file mode 100644 index 0000000000000..13f4f498f2189 --- /dev/null +++ b/tests/ui/reborrow/derive_coerce_shared_attr.stderr @@ -0,0 +1,45 @@ +error[E0802]: `derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute + --> $DIR/derive_coerce_shared_attr.rs:5:20 + | +LL | #[derive(Reborrow, CoerceShared)] + | ^^^^^^^^^^^^ + +error[E0802]: duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` + --> $DIR/derive_coerce_shared_attr.rs:11:1 + | +LL | #[coerce_shared(Target<'a>)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first `#[coerce_shared(Target)]` attribute is here + --> $DIR/derive_coerce_shared_attr.rs:10:1 + | +LL | #[coerce_shared(Target<'a>)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0802]: malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` + --> $DIR/derive_coerce_shared_attr.rs:16:1 + | +LL | #[coerce_shared] + | ^^^^^^^^^^^^^^^^ + | + = note: expected a single target type, for example `#[coerce_shared(Target<'a, T>)]` + +error[E0802]: malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` + --> $DIR/derive_coerce_shared_attr.rs:21:1 + | +LL | #[coerce_shared()] + | ^^^^^^^^^^^^^^^^^^ + | + = note: expected a single target type, for example `#[coerce_shared(Target<'a, T>)]` + +error[E0802]: malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)` + --> $DIR/derive_coerce_shared_attr.rs:26:1 + | +LL | #[coerce_shared(Target<'a>, Target<'a>)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected a single target type, for example `#[coerce_shared(Target<'a, T>)]` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0802`. diff --git a/tests/ui/reborrow/derive_invalid_coherence.rs b/tests/ui/reborrow/derive_invalid_coherence.rs new file mode 100644 index 0000000000000..c1680a964618e --- /dev/null +++ b/tests/ui/reborrow/derive_invalid_coherence.rs @@ -0,0 +1,26 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +#[derive(Reborrow)] +//~^ ERROR implementing `Reborrow` requires that a single lifetime parameter is passed between source and target +struct TooManyLifetimes<'a, 'b>(PhantomData<(&'a (), &'b ())>); + +#[derive(Clone, Copy)] +struct BadTarget<'a>(&'a ()); +//~^ ERROR implementing `CoerceShared` requires corresponding fields to match + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(BadTarget<'a>)] +struct BadSource<'a>(&'a mut u32); + +struct NotCopy; + +#[derive(Reborrow)] +struct NotCopyField<'a> { + field: NotCopy, + //~^ ERROR the trait bound `NotCopy: Copy` is not satisfied + marker: PhantomData<&'a ()>, +} + +fn main() {} diff --git a/tests/ui/reborrow/derive_invalid_coherence.stderr b/tests/ui/reborrow/derive_invalid_coherence.stderr new file mode 100644 index 0000000000000..9bbb5f6395524 --- /dev/null +++ b/tests/ui/reborrow/derive_invalid_coherence.stderr @@ -0,0 +1,33 @@ +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_invalid_coherence.rs:5:10 + | +LL | #[derive(Reborrow)] + | ^^^^^^^^ + +error[E0277]: the trait bound `NotCopy: Copy` is not satisfied + --> $DIR/derive_invalid_coherence.rs:21:5 + | +LL | field: NotCopy, + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopy` + | +help: consider annotating `NotCopy` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct NotCopy; + | + +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/derive_invalid_coherence.rs:10:22 + | +LL | struct BadTarget<'a>(&'a ()); + | ^^^^^^ target field `0` has type `&'a ()` +... +LL | #[derive(Reborrow, CoerceShared)] + | ------------ required by this `CoerceShared` implementation +LL | #[coerce_shared(BadTarget<'a>)] +LL | struct BadSource<'a>(&'a mut u32); + | ----------- source field `0` has type `&'a mut u32` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reborrow/derive_manual_equivalence.rs b/tests/ui/reborrow/derive_manual_equivalence.rs new file mode 100644 index 0000000000000..fdf62cc31ab76 --- /dev/null +++ b/tests/ui/reborrow/derive_manual_equivalence.rs @@ -0,0 +1,42 @@ +//@ run-pass + +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +struct DerivedRef<'a, T>(&'a T); +impl<'a, T> Clone for DerivedRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for DerivedRef<'a, T> {} + +#[derive(Reborrow, CoerceShared)] +#[coerce_shared(DerivedRef<'a, T>)] +struct DerivedMut<'a, T>(&'a mut T); + +struct ManualRef<'a, T>(&'a T); +impl<'a, T> Clone for ManualRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for ManualRef<'a, T> {} + +struct ManualMut<'a, T>(&'a mut T); + +impl<'a, T> Reborrow for ManualMut<'a, T> {} +impl<'a, T> CoerceShared> for ManualMut<'a, T> {} + +fn take_derived(_: DerivedRef<'_, ()>) {} +fn take_manual(_: ManualRef<'_, ()>) {} + +fn main() { + let derived = DerivedMut(&mut ()); + take_derived(derived); + + let manual = ManualMut(&mut ()); + take_manual(manual); +} diff --git a/tests/ui/reborrow/derive_multiple_lifetimes.rs b/tests/ui/reborrow/derive_multiple_lifetimes.rs new file mode 100644 index 0000000000000..2332896d6b03f --- /dev/null +++ b/tests/ui/reborrow/derive_multiple_lifetimes.rs @@ -0,0 +1,69 @@ +//@ normalize-stderr: "\n\n$" -> "\n" + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +// This test mirrors manual and derived impls with multiple lifetimes. The derive +// layer preserves both lifetimes; the current underlying trait validation rejects +// both forms with the same experimental one-lifetime limitation. + +struct ManualPair<'a, 'b, T> { + left: &'a mut T, + right: &'b mut T, +} + +impl<'a, 'b, T> Reborrow for ManualPair<'a, 'b, T> {} +//~^ ERROR implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + +#[derive(Reborrow)] +//~^ ERROR implementing `Reborrow` requires that a single lifetime parameter is passed between source and target +struct DerivedPair<'a, 'b, T> { + left: &'a mut T, + right: &'b mut T, +} + +struct ManualShared<'a, 'b, T> { + left: &'a T, + right: &'b T, +} + +impl<'a, 'b, T> Clone for ManualShared<'a, 'b, T> { + fn clone(&self) -> Self { + *self + } +} +impl<'a, 'b, T> Copy for ManualShared<'a, 'b, T> {} + +struct ManualMut<'a, 'b, T> { + left: &'a mut T, + right: &'b mut T, +} + +impl<'a, 'b, T> Reborrow for ManualMut<'a, 'b, T> {} +//~^ ERROR implementing `Reborrow` requires that a single lifetime parameter is passed between source and target +impl<'a, 'b, T> CoerceShared> for ManualMut<'a, 'b, T> {} +//~^ ERROR implementing `CoerceShared` requires that a single lifetime parameter is passed between source and target + +struct DerivedShared<'a, 'b, T> { + left: &'a T, + right: &'b T, +} + +impl<'a, 'b, T> Clone for DerivedShared<'a, 'b, T> { + fn clone(&self) -> Self { + *self + } +} +impl<'a, 'b, T> Copy for DerivedShared<'a, 'b, T> {} + +#[derive(Reborrow, CoerceShared)] +//~^ ERROR implementing `Reborrow` requires that a single lifetime parameter is passed between source and target +//~| ERROR implementing `CoerceShared` requires that a single lifetime parameter is passed between source and target +#[coerce_shared(DerivedShared<'a, 'b, T>)] +struct DerivedMut<'a, 'b, T> { + left: &'a mut T, + right: &'b mut T, +} + +fn main() {} diff --git a/tests/ui/reborrow/derive_multiple_lifetimes.stderr b/tests/ui/reborrow/derive_multiple_lifetimes.stderr new file mode 100644 index 0000000000000..b609ac6e718cb --- /dev/null +++ b/tests/ui/reborrow/derive_multiple_lifetimes.stderr @@ -0,0 +1,37 @@ +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_multiple_lifetimes.rs:16:1 + | +LL | impl<'a, 'b, T> Reborrow for ManualPair<'a, 'b, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_multiple_lifetimes.rs:19:10 + | +LL | #[derive(Reborrow)] + | ^^^^^^^^ + +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_multiple_lifetimes.rs:43:1 + | +LL | impl<'a, 'b, T> Reborrow for ManualMut<'a, 'b, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_multiple_lifetimes.rs:60:10 + | +LL | #[derive(Reborrow, CoerceShared)] + | ^^^^^^^^ + +error: implementing `CoerceShared` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_multiple_lifetimes.rs:45:1 + | +LL | impl<'a, 'b, T> CoerceShared> for ManualMut<'a, 'b, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: implementing `CoerceShared` requires that a single lifetime parameter is passed between source and target + --> $DIR/derive_multiple_lifetimes.rs:60:20 + | +LL | #[derive(Reborrow, CoerceShared)] + | ^^^^^^^^^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/reborrow/derive_reborrow.rs b/tests/ui/reborrow/derive_reborrow.rs new file mode 100644 index 0000000000000..6a4f50b82a167 --- /dev/null +++ b/tests/ui/reborrow/derive_reborrow.rs @@ -0,0 +1,50 @@ +//@ run-pass + +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{PhantomData, Reborrow}; + +#[derive(Reborrow)] +struct Named<'a, T> { + value: &'a mut T, +} + +#[derive(Reborrow)] +struct Tuple<'a, T>(&'a mut T); + +#[derive(Reborrow)] +struct Generic<'a, T = (), const N: usize = 1> +where + T: 'a, +{ + value: &'a mut [T; N], +} + +#[derive(Reborrow)] +struct Marker<'a>(PhantomData<&'a ()>); + +fn take_named(_: Named<'_, ()>) {} +fn take_tuple(_: Tuple<'_, ()>) {} +fn take_generic(_: Generic<'_>) {} +fn take_marker<'a>(_: Marker<'a>) -> &'a () { + &() +} + +fn main() { + let named = Named { value: &mut () }; + take_named(named); + take_named(named); + + let tuple = Tuple(&mut ()); + take_tuple(tuple); + take_tuple(tuple); + + let generic = Generic { value: &mut [()] }; + take_generic(generic); + take_generic(generic); + + let marker = Marker(PhantomData); + let _ = take_marker(marker); + let _ = take_marker(marker); +} diff --git a/tests/ui/reborrow/derive_unsupported_items.rs b/tests/ui/reborrow/derive_unsupported_items.rs new file mode 100644 index 0000000000000..5ebd92b5ba358 --- /dev/null +++ b/tests/ui/reborrow/derive_unsupported_items.rs @@ -0,0 +1,33 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +#[derive(Reborrow)] //~ ERROR `derive(Reborrow)` is only supported for structs, not enums +enum ReborrowEnum<'a> { + Variant(&'a mut ()), +} + +#[derive(CoerceShared)] //~ ERROR `derive(CoerceShared)` is only supported for structs, not enums +#[coerce_shared(CoerceSharedEnumRef<'a>)] +enum CoerceSharedEnum<'a> { + Variant(&'a mut ()), +} + +#[derive(Clone, Copy)] +struct CoerceSharedEnumRef<'a>(&'a ()); + +#[derive(Reborrow)] //~ ERROR `derive(Reborrow)` is only supported for structs, not unions +union ReborrowUnion<'a> { + field: &'a mut (), +} + +#[derive(CoerceShared)] //~ ERROR `derive(CoerceShared)` is only supported for structs, not unions +#[coerce_shared(CoerceSharedUnionRef<'a>)] +union CoerceSharedUnion<'a> { + field: &'a mut (), +} + +#[derive(Clone, Copy)] +struct CoerceSharedUnionRef<'a>(&'a ()); + +fn main() {} diff --git a/tests/ui/reborrow/derive_unsupported_items.stderr b/tests/ui/reborrow/derive_unsupported_items.stderr new file mode 100644 index 0000000000000..b079a2fbfff40 --- /dev/null +++ b/tests/ui/reborrow/derive_unsupported_items.stderr @@ -0,0 +1,27 @@ +error[E0802]: `derive(Reborrow)` is only supported for structs, not enums + --> $DIR/derive_unsupported_items.rs:5:10 + | +LL | #[derive(Reborrow)] + | ^^^^^^^^ + +error[E0802]: `derive(CoerceShared)` is only supported for structs, not enums + --> $DIR/derive_unsupported_items.rs:10:10 + | +LL | #[derive(CoerceShared)] + | ^^^^^^^^^^^^ + +error[E0802]: `derive(Reborrow)` is only supported for structs, not unions + --> $DIR/derive_unsupported_items.rs:19:10 + | +LL | #[derive(Reborrow)] + | ^^^^^^^^ + +error[E0802]: `derive(CoerceShared)` is only supported for structs, not unions + --> $DIR/derive_unsupported_items.rs:24:10 + | +LL | #[derive(CoerceShared)] + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0802`.