diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 4489a424c0d50..11afd359e5ad3 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -710,6 +710,12 @@ impl Pat { } } +impl From> for Pat { + fn from(value: P) -> Self { + *value + } +} + /// A single field in a struct pattern. /// /// Patterns like the fields of `Foo { x, ref y, ref mut z }` @@ -1553,17 +1559,23 @@ impl Expr { ) } - /// Creates a dummy `P`. + /// Creates a dummy `Expr`. /// /// Should only be used when it will be replaced afterwards or as a return value when an error was encountered. - pub fn dummy() -> P { - P(Expr { + pub fn dummy() -> Expr { + Expr { id: DUMMY_NODE_ID, kind: ExprKind::Dummy, span: DUMMY_SP, attrs: ThinVec::new(), tokens: None, - }) + } + } +} + +impl From> for Expr { + fn from(value: P) -> Self { + *value } } @@ -2374,6 +2386,12 @@ impl Clone for Ty { } } +impl From> for Ty { + fn from(value: P) -> Self { + *value + } +} + impl Ty { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 71a47dcfcba2b..07fbe8045fc2f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -168,7 +168,7 @@ pub trait MutVisitor: Sized + MutVisitorResult { walk_flat_map_arm(self, arm) } - fn visit_pat(&mut self, p: &mut P) { + fn visit_pat(&mut self, p: &mut Pat) { walk_pat(self, p); } @@ -176,7 +176,7 @@ pub trait MutVisitor: Sized + MutVisitorResult { walk_anon_const(self, c); } - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { walk_expr(self, e); } @@ -194,7 +194,7 @@ pub trait MutVisitor: Sized + MutVisitorResult { walk_generic_arg(self, arg); } - fn visit_ty(&mut self, t: &mut P) { + fn visit_ty(&mut self, t: &mut Ty) { walk_ty(self, t); } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 5ef76fb64aaf2..50eb7c7ae99bd 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -179,6 +179,8 @@ ast_lowering_underscore_expr_lhs_assign = in expressions, `_` can only be used on the left-hand side of an assignment .label = `_` not allowed here +ast_lowering_union_default_field_values = unions cannot have default field values + ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture ast_lowering_unstable_inline_assembly_label_operand_with_outputs = using both label and output operands for inline assembly is unstable diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 576fa9731e906..b444324ef9143 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -475,3 +475,10 @@ pub(crate) struct UseConstGenericArg { #[suggestion_part(code = "{other_args}")] pub call_args: Span, } + +#[derive(Diagnostic)] +#[diag(ast_lowering_union_default_field_values)] +pub(crate) struct UnionWithDefault { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 49110c93954ad..ef27d0ef69b14 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -17,6 +17,7 @@ use tracing::instrument; use super::errors::{ InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound, TupleStructWithDefault, + UnionWithDefault, }; use super::stability::{enabled_names, gate_unstable_abi}; use super::{ @@ -316,7 +317,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { this.arena.alloc_from_iter( - enum_definition.variants.iter().map(|x| this.lower_variant(x)), + enum_definition.variants.iter().map(|x| this.lower_variant(i, x)), ) }, ); @@ -328,7 +329,7 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| this.lower_variant_data(hir_id, struct_def), + |this| this.lower_variant_data(hir_id, i, struct_def), ); hir::ItemKind::Struct(ident, generics, struct_def) } @@ -338,7 +339,7 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| this.lower_variant_data(hir_id, vdata), + |this| this.lower_variant_data(hir_id, i, vdata), ); hir::ItemKind::Union(ident, generics, vdata) } @@ -714,13 +715,13 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { + fn lower_variant(&mut self, item_kind: &ItemKind, v: &Variant) -> hir::Variant<'hir> { let hir_id = self.lower_node_id(v.id); self.lower_attrs(hir_id, &v.attrs, v.span); hir::Variant { hir_id, def_id: self.local_def_id(v.id), - data: self.lower_variant_data(hir_id, &v.data), + data: self.lower_variant_data(hir_id, item_kind, &v.data), disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)), ident: self.lower_ident(v.ident), span: self.lower_span(v.span), @@ -730,15 +731,36 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_variant_data( &mut self, parent_id: hir::HirId, + item_kind: &ItemKind, vdata: &VariantData, ) -> hir::VariantData<'hir> { match vdata { - VariantData::Struct { fields, recovered } => hir::VariantData::Struct { - fields: self + VariantData::Struct { fields, recovered } => { + let fields = self .arena - .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))), - recovered: *recovered, - }, + .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))); + + if let ItemKind::Union(..) = item_kind { + for field in &fields[..] { + if let Some(default) = field.default { + // Unions cannot derive `Default`, and it's not clear how to use default + // field values of unions if that was supported. Therefore, blanket reject + // trying to use field values with unions. + if self.tcx.features().default_field_values() { + self.dcx().emit_err(UnionWithDefault { span: default.span }); + } else { + let _ = self.dcx().span_delayed_bug( + default.span, + "expected union default field values feature gate error but none \ + was produced", + ); + } + } + } + } + + hir::VariantData::Struct { fields, recovered: *recovered } + } VariantData::Tuple(fields, id) => { let ctor_id = self.lower_node_id(*id); self.alias_attrs(ctor_id, parent_id); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index c4b0f503664ee..095c0df98acc1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -342,10 +342,7 @@ impl<'tcx> BorrowExplanation<'tcx> { } } } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { - let sp = info - .span - .find_ancestor_in_same_ctxt(local_decl.source_info.span) - .unwrap_or(info.span); + let sp = info.span.find_oldest_ancestor_in_same_ctxt(); if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index da01e3e9607bb..fe44350863c9a 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -155,7 +155,7 @@ impl CfgEval<'_> { impl MutVisitor for CfgEval<'_> { #[instrument(level = "trace", skip(self))] - fn visit_expr(&mut self, expr: &mut P) { + fn visit_expr(&mut self, expr: &mut ast::Expr) { self.0.configure_expr(expr, false); mut_visit::walk_expr(self, expr); } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 0794192621a93..3a20b39798d7b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -1,5 +1,4 @@ use ast::HasAttrs; -use ast::ptr::P; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::BoundKind; use rustc_ast::{ @@ -378,11 +377,11 @@ struct TypeSubstitution<'a> { } impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { - fn visit_ty(&mut self, ty: &mut P) { + fn visit_ty(&mut self, ty: &mut ast::Ty) { if let Some(name) = ty.kind.is_simple_path() && name == self.from_name { - **ty = self.to_ty.clone(); + *ty = self.to_ty.clone(); self.rewritten = true; } else { ast::mut_visit::walk_ty(self, ty); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 3cfeb01ea4771..9fd524ef45cd0 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1768,7 +1768,7 @@ impl InvocationCollectorNode for ast::Crate { } } -impl InvocationCollectorNode for P { +impl InvocationCollectorNode for ast::Ty { type OutputTy = P; const KIND: AstFragmentKind = AstFragmentKind::Ty; fn to_annotatable(self) -> Annotatable { @@ -1791,7 +1791,7 @@ impl InvocationCollectorNode for P { } } -impl InvocationCollectorNode for P { +impl InvocationCollectorNode for ast::Pat { type OutputTy = P; const KIND: AstFragmentKind = AstFragmentKind::Pat; fn to_annotatable(self) -> Annotatable { @@ -1814,11 +1814,11 @@ impl InvocationCollectorNode for P { } } -impl InvocationCollectorNode for P { +impl InvocationCollectorNode for ast::Expr { type OutputTy = P; const KIND: AstFragmentKind = AstFragmentKind::Expr; fn to_annotatable(self) -> Annotatable { - Annotatable::Expr(self) + Annotatable::Expr(P(self)) } fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_expr() @@ -1955,29 +1955,29 @@ impl DummyAstNode for ast::Crate { } } -impl DummyAstNode for P { +impl DummyAstNode for ast::Ty { fn dummy() -> Self { - P(ast::Ty { + ast::Ty { id: DUMMY_NODE_ID, kind: TyKind::Dummy, span: Default::default(), tokens: Default::default(), - }) + } } } -impl DummyAstNode for P { +impl DummyAstNode for ast::Pat { fn dummy() -> Self { - P(ast::Pat { + ast::Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: Default::default(), tokens: Default::default(), - }) + } } } -impl DummyAstNode for P { +impl DummyAstNode for ast::Expr { fn dummy() -> Self { ast::Expr::dummy() } @@ -1985,7 +1985,7 @@ impl DummyAstNode for P { impl DummyAstNode for AstNodeWrapper, MethodReceiverTag> { fn dummy() -> Self { - AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag) + AstNodeWrapper::new(P(ast::Expr::dummy()), MethodReceiverTag) } } @@ -2272,7 +2272,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } - fn visit_node + DummyAstNode>( + fn visit_node> + DummyAstNode>( &mut self, node: &mut Node, ) { @@ -2297,6 +2297,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { *node = self .collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND) .make_ast::() + .into() } }, None if node.is_mac_call() => { @@ -2304,7 +2305,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let (mac, attrs, _) = n.take_mac_call(); self.check_attributes(&attrs, &mac); - *node = self.collect_bang(mac, Node::KIND).make_ast::() + *node = self.collect_bang(mac, Node::KIND).make_ast::().into() } None if node.delegation().is_some() => unreachable!(), None => { @@ -2414,15 +2415,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.visit_node(node) } - fn visit_ty(&mut self, node: &mut P) { + fn visit_ty(&mut self, node: &mut ast::Ty) { self.visit_node(node) } - fn visit_pat(&mut self, node: &mut P) { + fn visit_pat(&mut self, node: &mut ast::Pat) { self.visit_node(node) } - fn visit_expr(&mut self, node: &mut P) { + fn visit_expr(&mut self, node: &mut ast::Expr) { // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`. if let Some(attr) = node.attrs.first() { self.cfg().maybe_emit_expr_attr_err(attr); diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 3dcb20c8c7682..2c486a02bdf18 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -332,9 +332,9 @@ impl MutVisitor for PlaceholderExpander { } } - fn visit_expr(&mut self, expr: &mut P) { + fn visit_expr(&mut self, expr: &mut ast::Expr) { match expr.kind { - ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_expr(), + ast::ExprKind::MacCall(_) => *expr = *self.remove(expr.id).make_expr(), _ => walk_expr(self, expr), } } @@ -399,16 +399,16 @@ impl MutVisitor for PlaceholderExpander { stmts } - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut ast::Pat) { match pat.kind { - ast::PatKind::MacCall(_) => *pat = self.remove(pat.id).make_pat(), + ast::PatKind::MacCall(_) => *pat = *self.remove(pat.id).make_pat(), _ => walk_pat(self, pat), } } - fn visit_ty(&mut self, ty: &mut P) { + fn visit_ty(&mut self, ty: &mut ast::Ty) { match ty.kind { - ast::TyKind::MacCall(_) => *ty = self.remove(ty.id).make_ty(), + ast::TyKind::MacCall(_) => *ty = *self.remove(ty.id).make_ty(), _ => walk_ty(self, ty), } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1953eef81700b..0bc980b4d9f8f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1,7 +1,7 @@ // Decoding metadata from a single crate's metadata use std::iter::TrustedLen; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::{Arc, OnceLock}; use std::{io, iter, mem}; @@ -1610,10 +1610,14 @@ impl<'a> CrateMetadataRef<'a> { /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. fn imported_source_file(self, source_file_index: u32, sess: &Session) -> ImportedSourceFile { - fn filter<'a>(sess: &Session, path: Option<&'a Path>) -> Option<&'a Path> { + fn filter<'a>( + sess: &Session, + real_source_base_dir: &Option, + path: Option<&'a Path>, + ) -> Option<&'a Path> { path.filter(|_| { // Only spend time on further checks if we have what to translate *to*. - sess.opts.real_rust_source_base_dir.is_some() + real_source_base_dir.is_some() // Some tests need the translation to be always skipped. && sess.opts.unstable_opts.translate_remapped_path_to_local_path }) @@ -1625,57 +1629,92 @@ impl<'a> CrateMetadataRef<'a> { }) } - let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { - // Translate the virtual `/rustc/$hash` prefix back to a real directory - // that should hold actual sources, where possible. - // - // NOTE: if you update this, you might need to also update bootstrap's code for generating - // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. - let virtual_rust_source_base_dir = [ - filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), - filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), - ]; + let try_to_translate_virtual_to_real = + |virtual_source_base_dir: Option<&str>, + real_source_base_dir: &Option, + name: &mut rustc_span::FileName| { + let virtual_source_base_dir = [ + filter(sess, real_source_base_dir, virtual_source_base_dir.map(Path::new)), + filter( + sess, + real_source_base_dir, + sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref(), + ), + ]; - debug!( - "try_to_translate_virtual_to_real(name={:?}): \ - virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}", - name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir, - ); + debug!( + "try_to_translate_virtual_to_real(name={:?}): \ + virtual_source_base_dir={:?}, real_source_base_dir={:?}", + name, virtual_source_base_dir, real_source_base_dir, + ); - for virtual_dir in virtual_rust_source_base_dir.iter().flatten() { - if let Some(real_dir) = &sess.opts.real_rust_source_base_dir + for virtual_dir in virtual_source_base_dir.iter().flatten() { + if let Some(real_dir) = &real_source_base_dir + && let rustc_span::FileName::Real(old_name) = name + && let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } = + old_name + && let Ok(rest) = virtual_name.strip_prefix(virtual_dir) + { + let new_path = real_dir.join(rest); + + debug!( + "try_to_translate_virtual_to_real: `{}` -> `{}`", + virtual_name.display(), + new_path.display(), + ); + + // Check if the translated real path is affected by any user-requested + // remaps via --remap-path-prefix. Apply them if so. + // Note that this is a special case for imported rust-src paths specified by + // https://rust-lang.github.io/rfcs/3127-trim-paths.html#handling-sysroot-paths. + // Other imported paths are not currently remapped (see #66251). + let (user_remapped, applied) = + sess.source_map().path_mapping().map_prefix(&new_path); + let new_name = if applied { + rustc_span::RealFileName::Remapped { + local_path: Some(new_path.clone()), + virtual_name: user_remapped.to_path_buf(), + } + } else { + rustc_span::RealFileName::LocalPath(new_path) + }; + *old_name = new_name; + } + } + }; + + let try_to_translate_real_to_virtual = + |virtual_source_base_dir: Option<&str>, + real_source_base_dir: &Option, + subdir: &str, + name: &mut rustc_span::FileName| { + if let Some(virtual_dir) = &sess.opts.unstable_opts.simulate_remapped_rust_src_base + && let Some(real_dir) = real_source_base_dir && let rustc_span::FileName::Real(old_name) = name - && let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } = - old_name - && let Ok(rest) = virtual_name.strip_prefix(virtual_dir) { - let new_path = real_dir.join(rest); - - debug!( - "try_to_translate_virtual_to_real: `{}` -> `{}`", - virtual_name.display(), - new_path.display(), - ); - - // Check if the translated real path is affected by any user-requested - // remaps via --remap-path-prefix. Apply them if so. - // Note that this is a special case for imported rust-src paths specified by - // https://rust-lang.github.io/rfcs/3127-trim-paths.html#handling-sysroot-paths. - // Other imported paths are not currently remapped (see #66251). - let (user_remapped, applied) = - sess.source_map().path_mapping().map_prefix(&new_path); - let new_name = if applied { - rustc_span::RealFileName::Remapped { - local_path: Some(new_path.clone()), - virtual_name: user_remapped.to_path_buf(), + let relative_path = match old_name { + rustc_span::RealFileName::LocalPath(local) => { + local.strip_prefix(real_dir).ok() + } + rustc_span::RealFileName::Remapped { virtual_name, .. } => { + virtual_source_base_dir + .and_then(|virtual_dir| virtual_name.strip_prefix(virtual_dir).ok()) } - } else { - rustc_span::RealFileName::LocalPath(new_path) }; - *old_name = new_name; + debug!( + ?relative_path, + ?virtual_dir, + ?subdir, + "simulate_remapped_rust_src_base" + ); + if let Some(rest) = relative_path.and_then(|p| p.strip_prefix(subdir).ok()) { + *old_name = rustc_span::RealFileName::Remapped { + local_path: None, + virtual_name: virtual_dir.join(subdir).join(rest), + }; + } } - } - }; + }; let mut import_info = self.cdata.source_map_import_info.lock(); for _ in import_info.len()..=(source_file_index as usize) { @@ -1713,36 +1752,45 @@ impl<'a> CrateMetadataRef<'a> { // This is useful for testing so that tests about the effects of // `try_to_translate_virtual_to_real` don't have to worry about how the // compiler is bootstrapped. - if let Some(virtual_dir) = &sess.opts.unstable_opts.simulate_remapped_rust_src_base - && let Some(real_dir) = &sess.opts.real_rust_source_base_dir - && let rustc_span::FileName::Real(ref mut old_name) = name - { - let relative_path = match old_name { - rustc_span::RealFileName::LocalPath(local) => { - local.strip_prefix(real_dir).ok() - } - rustc_span::RealFileName::Remapped { virtual_name, .. } => { - option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR") - .and_then(|virtual_dir| virtual_name.strip_prefix(virtual_dir).ok()) - } - }; - debug!(?relative_path, ?virtual_dir, "simulate_remapped_rust_src_base"); - for subdir in ["library", "compiler"] { - if let Some(rest) = relative_path.and_then(|p| p.strip_prefix(subdir).ok()) - { - *old_name = rustc_span::RealFileName::Remapped { - local_path: None, // FIXME: maybe we should preserve this? - virtual_name: virtual_dir.join(subdir).join(rest), - }; - break; - } - } - } + try_to_translate_real_to_virtual( + option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"), + &sess.opts.real_rust_source_base_dir, + "library", + &mut name, + ); + + // If this file is under $sysroot/lib/rustlib/rustc-src/ + // and the user wish to simulate remapping with -Z simulate-remapped-rust-src-base, + // then we change `name` to a similar state as if the rust was bootstrapped + // with `remap-debuginfo = true`. + try_to_translate_real_to_virtual( + option_env!("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR"), + &sess.opts.real_rustc_dev_source_base_dir, + "compiler", + &mut name, + ); // If this file's path has been remapped to `/rustc/$hash`, - // we might be able to reverse that (also see comments above, - // on `try_to_translate_virtual_to_real`). - try_to_translate_virtual_to_real(&mut name); + // we might be able to reverse that. + // + // NOTE: if you update this, you might need to also update bootstrap's code for generating + // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. + try_to_translate_virtual_to_real( + option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"), + &sess.opts.real_rust_source_base_dir, + &mut name, + ); + + // If this file's path has been remapped to `/rustc-dev/$hash`, + // we might be able to reverse that. + // + // NOTE: if you update this, you might need to also update bootstrap's code for generating + // the `rustc-dev` component in `Src::run` in `src/bootstrap/dist.rs`. + try_to_translate_virtual_to_real( + option_env!("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR"), + &sess.opts.real_rustc_dev_source_base_dir, + &mut name, + ); let local_version = sess.source_map().new_imported_source_file( name, diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8f88228d9bbd8..a54e548ad70ab 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -74,7 +74,11 @@ impl SimplifyCfg { } pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - CfgSimplifier::new(tcx, body).simplify(); + if CfgSimplifier::new(tcx, body).simplify() { + // `simplify` returns that it changed something. We must invalidate the CFG caches as they + // are not consistent with the modified CFG any more. + body.basic_blocks.invalidate_cfg_cache(); + } remove_dead_blocks(body); // FIXME: Should probably be moved into some kind of pass manager @@ -121,12 +125,16 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`. let preserve_switch_reads = matches!(body.phase, MirPhase::Built | MirPhase::Analysis(_)) || tcx.sess.opts.unstable_opts.mir_preserve_ub; - let basic_blocks = body.basic_blocks_mut(); + // Do not clear caches yet. The caller to `simplify` will do it if anything changed. + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); CfgSimplifier { preserve_switch_reads, basic_blocks, pred_count } } - fn simplify(mut self) { + /// Returns whether we actually simplified anything. In that case, the caller *must* invalidate + /// the CFG caches of the MIR body. + #[must_use] + fn simplify(mut self) -> bool { self.strip_nops(); // Vec of the blocks that should be merged. We store the indices here, instead of the @@ -134,6 +142,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // We do not push the statements directly into the target block (`bb`) as that is slower // due to additional reallocations let mut merged_blocks = Vec::new(); + let mut outer_changed = false; loop { let mut changed = false; @@ -177,7 +186,11 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { if !changed { break; } + + outer_changed = true; } + + outer_changed } /// This function will return `None` if diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93489aa8ee948..93c76c47f060b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4087,7 +4087,7 @@ impl<'a> CondChecker<'a> { } impl MutVisitor for CondChecker<'_> { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { self.depth += 1; use ForbiddenLetReason::*; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7a226136e2353..64653ee2a04c9 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> { fn make_all_value_bindings_mutable(pat: &mut P) -> bool { struct AddMut(bool); impl MutVisitor for AddMut { - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut Pat) { if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 406a6bd335a74..b7fb5b690f033 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1364,6 +1364,7 @@ impl Default for Options { cli_forced_local_thinlto_off: false, remap_path_prefix: Vec::new(), real_rust_source_base_dir: None, + real_rustc_dev_source_base_dir: None, edition: DEFAULT_EDITION, json_artifact_notifications: false, json_unused_externs: JsonUnusedExterns::No, @@ -2701,9 +2702,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let sysroot = filesearch::materialize_sysroot(sysroot_opt); - let real_rust_source_base_dir = { - // This is the location used by the `rust-src` `rustup` component. - let mut candidate = sysroot.join("lib/rustlib/src/rust"); + let real_source_base_dir = |suffix: &str, confirm: &str| { + let mut candidate = sysroot.join(suffix); if let Ok(metadata) = candidate.symlink_metadata() { // Replace the symlink bootstrap creates, with its destination. // We could try to use `fs::canonicalize` instead, but that might @@ -2716,9 +2716,17 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } // Only use this directory if it has a file we can expect to always find. - candidate.join("library/std/src/lib.rs").is_file().then_some(candidate) + candidate.join(confirm).is_file().then_some(candidate) }; + let real_rust_source_base_dir = + // This is the location used by the `rust-src` `rustup` component. + real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs"); + + let real_rustc_dev_source_base_dir = + // This is the location used by the `rustc-dev` `rustup` component. + real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs"); + let mut search_paths = vec![]; for s in &matches.opt_strs("L") { search_paths.push(SearchPath::from_cli_opt( @@ -2772,6 +2780,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M cli_forced_local_thinlto_off: disable_local_thinlto, remap_path_prefix, real_rust_source_base_dir, + real_rustc_dev_source_base_dir, edition, json_artifact_notifications, json_unused_externs, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f76f258d00de9..65c09cd72fbb5 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -395,15 +395,25 @@ top_level_options!( /// Remap source path prefixes in all output (messages, object files, debug, etc.). remap_path_prefix: Vec<(PathBuf, PathBuf)> [TRACKED_NO_CRATE_HASH], - /// Base directory containing the `src/` for the Rust standard library, and - /// potentially `rustc` as well, if we can find it. Right now it's always - /// `$sysroot/lib/rustlib/src/rust` (i.e. the `rustup` `rust-src` component). + + /// Base directory containing the `library/` directory for the Rust standard library. + /// Right now it's always `$sysroot/lib/rustlib/src/rust` + /// (i.e. the `rustup` `rust-src` component). /// /// This directory is what the virtual `/rustc/$hash` is translated back to, /// if Rust was built with path remapping to `/rustc/$hash` enabled /// (the `rust.remap-debuginfo` option in `bootstrap.toml`). real_rust_source_base_dir: Option [TRACKED_NO_CRATE_HASH], + /// Base directory containing the `compiler/` directory for the rustc sources. + /// Right now it's always `$sysroot/lib/rustlib/rustc-src/rust` + /// (i.e. the `rustup` `rustc-dev` component). + /// + /// This directory is what the virtual `/rustc-dev/$hash` is translated back to, + /// if Rust was built with path remapping to `/rustc/$hash` enabled + /// (the `rust.remap-debuginfo` option in `bootstrap.toml`). + real_rustc_dev_source_base_dir: Option [TRACKED_NO_CRATE_HASH], + edition: Edition [TRACKED], /// `true` if we're emitting JSON blobs about each artifact produced diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 37614a7ca4571..7c71594c43035 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1489,10 +1489,11 @@ impl String { Some(ch) } - /// Removes a [`char`] from this `String` at a byte position and returns it. + /// Removes a [`char`] from this `String` at byte position `idx` and returns it. /// - /// This is an *O*(*n*) operation, as it requires copying every element in the - /// buffer. + /// Copies all bytes after the removed char to new positions. + /// + /// Note that calling this in a loop can result in quadratic behavior. /// /// # Panics /// @@ -1678,10 +1679,13 @@ impl String { drop(guard); } - /// Inserts a character into this `String` at a byte position. + /// Inserts a character into this `String` at byte position `idx`. + /// + /// Reallocates if `self.capacity()` is insufficient, which may involve copying all + /// `self.capacity()` bytes. Makes space for the insertion by copying all bytes of + /// `&self[idx..]` to new positions. /// - /// This is an *O*(*n*) operation as it requires copying every element in the - /// buffer. + /// Note that calling this in a loop can result in quadratic behavior. /// /// # Panics /// @@ -1733,10 +1737,13 @@ impl String { } } - /// Inserts a string slice into this `String` at a byte position. + /// Inserts a string slice into this `String` at byte position `idx`. + /// + /// Reallocates if `self.capacity()` is insufficient, which may involve copying all + /// `self.capacity()` bytes. Makes space for the insertion by copying all bytes of + /// `&self[idx..]` to new positions. /// - /// This is an *O*(*n*) operation as it requires copying every element in the - /// buffer. + /// Note that calling this in a loop can result in quadratic behavior. /// /// # Panics /// diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index a4b6efe35fc14..a2c1ba835f366 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -719,7 +719,7 @@ impl Cell<[T; N]> { #[rustc_diagnostic_item = "RefCell"] #[stable(feature = "rust1", since = "1.0.0")] pub struct RefCell { - borrow: Cell, + borrow: Cell, // Stores the location of the earliest currently active borrow. // This gets updated whenever we go from having zero borrows // to having a single borrow. When a borrow occurs, this gets included @@ -732,54 +732,48 @@ pub struct RefCell { /// An error returned by [`RefCell::try_borrow`]. #[stable(feature = "try_borrow", since = "1.13.0")] #[non_exhaustive] +#[derive(Debug)] pub struct BorrowError { #[cfg(feature = "debug_refcell")] location: &'static crate::panic::Location<'static>, } #[stable(feature = "try_borrow", since = "1.13.0")] -impl Debug for BorrowError { +impl Display for BorrowError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = f.debug_struct("BorrowError"); - #[cfg(feature = "debug_refcell")] - builder.field("location", self.location); + let res = write!( + f, + "RefCell already mutably borrowed; a previous borrow was at {}", + self.location + ); - builder.finish() - } -} + #[cfg(not(feature = "debug_refcell"))] + let res = Display::fmt("RefCell already mutably borrowed", f); -#[stable(feature = "try_borrow", since = "1.13.0")] -impl Display for BorrowError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt("already mutably borrowed", f) + res } } /// An error returned by [`RefCell::try_borrow_mut`]. #[stable(feature = "try_borrow", since = "1.13.0")] #[non_exhaustive] +#[derive(Debug)] pub struct BorrowMutError { #[cfg(feature = "debug_refcell")] location: &'static crate::panic::Location<'static>, } #[stable(feature = "try_borrow", since = "1.13.0")] -impl Debug for BorrowMutError { +impl Display for BorrowMutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = f.debug_struct("BorrowMutError"); - #[cfg(feature = "debug_refcell")] - builder.field("location", self.location); + let res = write!(f, "RefCell already borrowed; a previous borrow was at {}", self.location); - builder.finish() - } -} + #[cfg(not(feature = "debug_refcell"))] + let res = Display::fmt("RefCell already borrowed", f); -#[stable(feature = "try_borrow", since = "1.13.0")] -impl Display for BorrowMutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt("already borrowed", f) + res } } @@ -788,7 +782,7 @@ impl Display for BorrowMutError { #[track_caller] #[cold] fn panic_already_borrowed(err: BorrowMutError) -> ! { - panic!("already borrowed: {:?}", err) + panic!("{err}") } // This ensures the panicking code is outlined from `borrow` for `RefCell`. @@ -796,7 +790,7 @@ fn panic_already_borrowed(err: BorrowMutError) -> ! { #[track_caller] #[cold] fn panic_already_mutably_borrowed(err: BorrowError) -> ! { - panic!("already mutably borrowed: {:?}", err) + panic!("{err}") } // Positive values represent the number of `Ref` active. Negative values @@ -806,22 +800,22 @@ fn panic_already_mutably_borrowed(err: BorrowError) -> ! { // // `Ref` and `RefMut` are both two words in size, and so there will likely never // be enough `Ref`s or `RefMut`s in existence to overflow half of the `usize` -// range. Thus, a `BorrowFlag` will probably never overflow or underflow. +// range. Thus, a `BorrowCounter` will probably never overflow or underflow. // However, this is not a guarantee, as a pathological program could repeatedly // create and then mem::forget `Ref`s or `RefMut`s. Thus, all code must // explicitly check for overflow and underflow in order to avoid unsafety, or at // least behave correctly in the event that overflow or underflow happens (e.g., // see BorrowRef::new). -type BorrowFlag = isize; -const UNUSED: BorrowFlag = 0; +type BorrowCounter = isize; +const UNUSED: BorrowCounter = 0; #[inline(always)] -fn is_writing(x: BorrowFlag) -> bool { +fn is_writing(x: BorrowCounter) -> bool { x < UNUSED } #[inline(always)] -fn is_reading(x: BorrowFlag) -> bool { +fn is_reading(x: BorrowCounter) -> bool { x > UNUSED } @@ -1401,12 +1395,12 @@ impl From for RefCell { impl, U> CoerceUnsized> for RefCell {} struct BorrowRef<'b> { - borrow: &'b Cell, + borrow: &'b Cell, } impl<'b> BorrowRef<'b> { #[inline] - fn new(borrow: &'b Cell) -> Option> { + fn new(borrow: &'b Cell) -> Option> { let b = borrow.get().wrapping_add(1); if !is_reading(b) { // Incrementing borrow can result in a non-reading value (<= 0) in these cases: @@ -1447,7 +1441,7 @@ impl Clone for BorrowRef<'_> { debug_assert!(is_reading(borrow)); // Prevent the borrow counter from overflowing into // a writing borrow. - assert!(borrow != BorrowFlag::MAX); + assert!(borrow != BorrowCounter::MAX); self.borrow.set(borrow + 1); BorrowRef { borrow: self.borrow } } @@ -1795,7 +1789,7 @@ impl<'b, T: ?Sized> RefMut<'b, T> { } struct BorrowRefMut<'b> { - borrow: &'b Cell, + borrow: &'b Cell, } impl Drop for BorrowRefMut<'_> { @@ -1809,7 +1803,7 @@ impl Drop for BorrowRefMut<'_> { impl<'b> BorrowRefMut<'b> { #[inline] - fn new(borrow: &'b Cell) -> Option> { + fn new(borrow: &'b Cell) -> Option> { // NOTE: Unlike BorrowRefMut::clone, new is called to create the initial // mutable reference, and so there must currently be no existing // references. Thus, while clone increments the mutable refcount, here @@ -1833,7 +1827,7 @@ impl<'b> BorrowRefMut<'b> { let borrow = self.borrow.get(); debug_assert!(is_writing(borrow)); // Prevent the borrow counter from underflowing. - assert!(borrow != BorrowFlag::MIN); + assert!(borrow != BorrowCounter::MIN); self.borrow.set(borrow - 1); BorrowRefMut { borrow: self.borrow } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 1fae5b8390247..8f1000ca0b8bc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -20,19 +20,24 @@ use crate::{fmt, hash, intrinsics, mem, ptr}; /// as a discriminant -- `Option>` has the same size as `*mut T`. /// However the pointer may still dangle if it isn't dereferenced. /// -/// Unlike `*mut T`, `NonNull` was chosen to be covariant over `T`. This makes it -/// possible to use `NonNull` when building covariant types, but introduces the -/// risk of unsoundness if used in a type that shouldn't actually be covariant. -/// (The opposite choice was made for `*mut T` even though technically the unsoundness -/// could only be caused by calling unsafe functions.) +/// Unlike `*mut T`, `NonNull` is covariant over `T`. This is usually the correct +/// choice for most data structures and safe abstractions, such as `Box`, `Rc`, `Arc`, `Vec`, +/// and `LinkedList`. /// -/// Covariance is correct for most safe abstractions, such as `Box`, `Rc`, `Arc`, `Vec`, -/// and `LinkedList`. This is the case because they provide a public API that follows the -/// normal shared XOR mutable rules of Rust. +/// In rare cases, if your type exposes a way to mutate the value of `T` through a `NonNull`, +/// and you need to prevent unsoundness from variance (for example, if `T` could be a reference +/// with a shorter lifetime), you should add a field to make your type invariant, such as +/// `PhantomData>` or `PhantomData<&'a mut T>`. /// -/// If your type cannot safely be covariant, you must ensure it contains some -/// additional field to provide invariance. Often this field will be a [`PhantomData`] -/// type like `PhantomData>` or `PhantomData<&'a mut T>`. +/// Example of a type that must be invariant: +/// ```rust +/// use std::cell::Cell; +/// use std::marker::PhantomData; +/// struct Invariant { +/// ptr: std::ptr::NonNull, +/// _invariant: PhantomData>, +/// } +/// ``` /// /// Notice that `NonNull` has a `From` instance for `&T`. However, this does /// not change the fact that mutating through a (pointer derived from a) shared diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index ac1c5e9932e1c..53dec105d0c85 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -119,6 +119,23 @@ unsafe extern "system" { pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; } +windows_targets::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile( + filehandle: *mut HANDLE, + desiredaccess: FILE_ACCESS_RIGHTS, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + shareaccess: FILE_SHARE_MODE, + createdisposition: NTCREATEFILE_CREATE_DISPOSITION, + createoptions: NTCREATEFILE_CREATE_OPTIONS, + namedpipetype: u32, + readmode: u32, + completionmode: u32, + maximuminstances: u32, + inboundquota: u32, + outboundquota: u32, + defaulttimeout: *const u64, +) -> NTSTATUS); + // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index a99c474c763c5..827d96e73db41 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2060,6 +2060,14 @@ FILE_OPEN_REPARSE_POINT FILE_OPEN_REQUIRING_OPLOCK FILE_OVERWRITE FILE_OVERWRITE_IF +FILE_PIPE_ACCEPT_REMOTE_CLIENTS +FILE_PIPE_BYTE_STREAM_MODE +FILE_PIPE_BYTE_STREAM_TYPE +FILE_PIPE_COMPLETE_OPERATION +FILE_PIPE_MESSAGE_MODE +FILE_PIPE_MESSAGE_TYPE +FILE_PIPE_QUEUE_OPERATION +FILE_PIPE_REJECT_REMOTE_CLIENTS FILE_RANDOM_ACCESS FILE_READ_ATTRIBUTES FILE_READ_DATA @@ -2294,7 +2302,16 @@ NtOpenFile NtReadFile NTSTATUS NtWriteFile +OBJ_CASE_INSENSITIVE OBJ_DONT_REPARSE +OBJ_EXCLUSIVE +OBJ_FORCE_ACCESS_CHECK +OBJ_IGNORE_IMPERSONATED_DEVICEMAP +OBJ_INHERIT +OBJ_KERNEL_HANDLE +OBJ_OPENIF +OBJ_OPENLINK +OBJ_PERMANENT OPEN_ALWAYS OPEN_EXISTING OpenProcessToken diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 95bf8040229d0..b2e3aabc63353 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.61.0 +// Bindings generated by `windows-bindgen` 0.61.1 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] @@ -2552,6 +2552,14 @@ pub const FILE_OPEN_REPARSE_POINT: NTCREATEFILE_CREATE_OPTIONS = 2097152u32; pub const FILE_OPEN_REQUIRING_OPLOCK: NTCREATEFILE_CREATE_OPTIONS = 65536u32; pub const FILE_OVERWRITE: NTCREATEFILE_CREATE_DISPOSITION = 4u32; pub const FILE_OVERWRITE_IF: NTCREATEFILE_CREATE_DISPOSITION = 5u32; +pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS: u32 = 0u32; +pub const FILE_PIPE_BYTE_STREAM_MODE: u32 = 0u32; +pub const FILE_PIPE_BYTE_STREAM_TYPE: u32 = 0u32; +pub const FILE_PIPE_COMPLETE_OPERATION: u32 = 1u32; +pub const FILE_PIPE_MESSAGE_MODE: u32 = 1u32; +pub const FILE_PIPE_MESSAGE_TYPE: u32 = 1u32; +pub const FILE_PIPE_QUEUE_OPERATION: u32 = 0u32; +pub const FILE_PIPE_REJECT_REMOTE_CLIENTS: u32 = 2u32; pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32; pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32; pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32; @@ -2983,7 +2991,16 @@ impl Default for OBJECT_ATTRIBUTES { } } pub type OBJECT_ATTRIBUTE_FLAGS = u32; +pub const OBJ_CASE_INSENSITIVE: OBJECT_ATTRIBUTE_FLAGS = 64u32; pub const OBJ_DONT_REPARSE: OBJECT_ATTRIBUTE_FLAGS = 4096u32; +pub const OBJ_EXCLUSIVE: OBJECT_ATTRIBUTE_FLAGS = 32u32; +pub const OBJ_FORCE_ACCESS_CHECK: OBJECT_ATTRIBUTE_FLAGS = 1024u32; +pub const OBJ_IGNORE_IMPERSONATED_DEVICEMAP: OBJECT_ATTRIBUTE_FLAGS = 2048u32; +pub const OBJ_INHERIT: OBJECT_ATTRIBUTE_FLAGS = 2u32; +pub const OBJ_KERNEL_HANDLE: OBJECT_ATTRIBUTE_FLAGS = 512u32; +pub const OBJ_OPENIF: OBJECT_ATTRIBUTE_FLAGS = 128u32; +pub const OBJ_OPENLINK: OBJECT_ATTRIBUTE_FLAGS = 256u32; +pub const OBJ_PERMANENT: OBJECT_ATTRIBUTE_FLAGS = 16u32; pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; #[repr(C)] diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 00d469fbaf8c7..bc5d05c45052a 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -1,14 +1,9 @@ -use crate::ffi::OsStr; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::ops::Neg; use crate::os::windows::prelude::*; -use crate::path::Path; -use crate::random::{DefaultRandomSource, Random}; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::atomic::{Atomic, AtomicUsize}; +use crate::sys::api::utf16; use crate::sys::c; -use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; -use crate::sys::pal::windows::api::{self, WinError}; use crate::sys_common::{FromInner, IntoInner}; use crate::{mem, ptr}; @@ -62,92 +57,113 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // Note that we specifically do *not* use `CreatePipe` here because // unfortunately the anonymous pipes returned do not support overlapped - // operations. Instead, we create a "hopefully unique" name and create a - // named pipe which has overlapped operations enabled. + // operations. Instead, we use `NtCreateNamedPipeFile` to create the + // anonymous pipe with overlapped support. // - // Once we do this, we connect do it as usual via `CreateFileW`, and then + // Once we do this, we connect to it via `NtOpenFile`, and then // we return those reader/writer halves. Note that the `ours` pipe return // value is always the named pipe, whereas `theirs` is just the normal file. // This should hopefully shield us from child processes which assume their // stdout is a named pipe, which would indeed be odd! unsafe { - let ours; - let mut name; - let mut tries = 0; - loop { - tries += 1; - name = format!( - r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", - c::GetCurrentProcessId(), - random_number(), + let mut io_status = c::IO_STATUS_BLOCK::default(); + let mut object_attributes = c::OBJECT_ATTRIBUTES::default(); + object_attributes.Length = size_of::() as u32; + + // Open a handle to the pipe filesystem (`\??\PIPE\`). + // This will be used when creating a new annon pipe. + let pipe_fs = { + let path = c::UNICODE_STRING::from_ref(utf16!(r"\??\PIPE\")); + object_attributes.ObjectName = &path; + let mut pipe_fs = ptr::null_mut(); + let status = c::NtOpenFile( + &mut pipe_fs, + c::SYNCHRONIZE | c::GENERIC_READ, + &object_attributes, + &mut io_status, + c::FILE_SHARE_READ | c::FILE_SHARE_WRITE, + c::FILE_SYNCHRONOUS_IO_NONALERT, // synchronous access ); - let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::>(); - let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED; - if ours_readable { - flags |= c::PIPE_ACCESS_INBOUND; + if c::nt_success(status) { + Handle::from_raw_handle(pipe_fs) } else { - flags |= c::PIPE_ACCESS_OUTBOUND; + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); } + }; - let handle = c::CreateNamedPipeW( - wide_name.as_ptr(), - flags, - c::PIPE_TYPE_BYTE - | c::PIPE_READMODE_BYTE - | c::PIPE_WAIT - | c::PIPE_REJECT_REMOTE_CLIENTS, + // From now on we're using handles instead of paths to create and open pipes. + // So set the `ObjectName` to a zero length string. + let empty = c::UNICODE_STRING::default(); + object_attributes.ObjectName = ∅ + + // Create our side of the pipe for async access. + let ours = { + // Use the pipe filesystem as the root directory. + // With no name provided, an anonymous pipe will be created. + object_attributes.RootDirectory = pipe_fs.as_raw_handle(); + + // A negative timeout value is a relative time (rather than an absolute time). + // The time is given in 100's of nanoseconds so this is 50 milliseconds. + // This value was chosen to be consistent with the default timeout set by `CreateNamedPipeW` + // See: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew + let timeout = (50_i64 * 10000).neg() as u64; + + let mut ours = ptr::null_mut(); + let status = c::NtCreateNamedPipeFile( + &mut ours, + c::SYNCHRONIZE | if ours_readable { c::GENERIC_READ } else { c::GENERIC_WRITE }, + &object_attributes, + &mut io_status, + if ours_readable { c::FILE_SHARE_WRITE } else { c::FILE_SHARE_READ }, + c::FILE_CREATE, + 0, + c::FILE_PIPE_BYTE_STREAM_TYPE, + c::FILE_PIPE_BYTE_STREAM_MODE, + c::FILE_PIPE_QUEUE_OPERATION, + // only allow one client pipe 1, PIPE_BUFFER_CAPACITY, PIPE_BUFFER_CAPACITY, - 0, - ptr::null_mut(), + &timeout, ); - - // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're - // also just doing a best effort at selecting a unique name. If - // `ERROR_ACCESS_DENIED` is returned then it could mean that we - // accidentally conflicted with an already existing pipe, so we try - // again. - // - // Don't try again too much though as this could also perhaps be a - // legit error. - if handle == c::INVALID_HANDLE_VALUE { - let error = api::get_last_error(); - if tries < 10 && error == WinError::ACCESS_DENIED { - continue; - } else { - return Err(io::Error::from_raw_os_error(error.code as i32)); - } + if c::nt_success(status) { + Handle::from_raw_handle(ours) + } else { + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); } + }; - ours = Handle::from_raw_handle(handle); - break; - } + // Open their side of the pipe for synchronous access. + let theirs = { + // We can reopen the anonymous pipe without a name by setting + // RootDirectory to the pipe handle and not setting a path name, + object_attributes.RootDirectory = ours.as_raw_handle(); - // Connect to the named pipe we just created. This handle is going to be - // returned in `theirs`, so if `ours` is readable we want this to be - // writable, otherwise if `ours` is writable we want this to be - // readable. - // - // Additionally we don't enable overlapped mode on this because most - // client processes aren't enabled to work with that. - let mut opts = OpenOptions::new(); - opts.write(ours_readable); - opts.read(!ours_readable); - opts.share_mode(0); - let size = size_of::(); - let mut sa = c::SECURITY_ATTRIBUTES { - nLength: size as u32, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: their_handle_inheritable as i32, + if their_handle_inheritable { + object_attributes.Attributes |= c::OBJ_INHERIT; + } + let mut theirs = ptr::null_mut(); + let status = c::NtOpenFile( + &mut theirs, + c::SYNCHRONIZE + | if ours_readable { + c::GENERIC_WRITE | c::FILE_READ_ATTRIBUTES + } else { + c::GENERIC_READ + }, + &object_attributes, + &mut io_status, + 0, + c::FILE_NON_DIRECTORY_FILE | c::FILE_SYNCHRONOUS_IO_NONALERT, + ); + if c::nt_success(status) { + Handle::from_raw_handle(theirs) + } else { + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); + } }; - opts.security_attributes(&mut sa); - let theirs = File::open(Path::new(&name), &opts)?; - Ok(Pipes { - ours: AnonPipe { inner: ours }, - theirs: AnonPipe { inner: theirs.into_inner() }, - }) + Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } }) } } @@ -191,17 +207,6 @@ pub fn spawn_pipe_relay( Ok(theirs) } -fn random_number() -> usize { - static N: Atomic = AtomicUsize::new(0); - loop { - if N.load(Relaxed) != 0 { - return N.fetch_add(1, Relaxed); - } - - N.store(usize::random(&mut DefaultRandomSource), Relaxed); - } -} - impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 25d3efdbb8260..9e4a11044e818 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -113,6 +113,8 @@ Compiletest makes the following replacements on the compiler output: - The base directory where the test's output goes is replaced with `$TEST_BUILD_DIR`. This only comes up in a few rare circumstances. Example: `/path/to/rust/build/x86_64-unknown-linux-gnu/test/ui` +- The real directory to the standard library source is replaced with `$SRC_DIR_REAL`. +- The real directory to the compiler source is replaced with `$COMPILER_DIR_REAL`. - Tabs are replaced with `\t`. - Backslashes (`\`) are converted to forward slashes (`/`) within paths (using a heuristic). This helps normalize differences with Windows-style paths. diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index b839b6f56728c..bd8420917f5e6 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -128,7 +128,7 @@ fn remove_all_parens(pat: &mut P) { } impl MutVisitor for Visitor { - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut Pat) { let is_inner = mem::replace(&mut self.is_inner, true); walk_pat(self, pat); let inner = match &mut pat.kind { @@ -145,7 +145,7 @@ fn remove_all_parens(pat: &mut P) { fn insert_necessary_parens(pat: &mut P) { struct Visitor; impl MutVisitor for Visitor { - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut Pat) { use ast::BindingMode; walk_pat(self, pat); let target = match &mut pat.kind { @@ -167,7 +167,7 @@ fn unnest_or_patterns(pat: &mut P) -> bool { changed: bool, } impl MutVisitor for Visitor { - fn visit_pat(&mut self, p: &mut P) { + fn visit_pat(&mut self, p: &mut Pat) { // This is a bottom up transformation, so recurse first. walk_pat(self, p); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 75f24adb70fa5..9edcba5d460c1 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2371,6 +2371,12 @@ impl<'test> TestCx<'test> { let rust_src_dir = rust_src_dir.read_link_utf8().unwrap_or(rust_src_dir.to_path_buf()); normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL"); + // Real paths into the compiler + let rustc_src_dir = &self.config.sysroot_base.join("lib/rustlib/rustc-src/rust"); + rustc_src_dir.try_exists().expect(&*format!("{} should exists", rustc_src_dir)); + let rustc_src_dir = rustc_src_dir.read_link_utf8().unwrap_or(rustc_src_dir.to_path_buf()); + normalize_path(&rustc_src_dir.join("compiler"), "$COMPILER_DIR_REAL"); + // eg. // /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui//$name.$revision.$mode/ normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR"); diff --git a/tests/run-make/dump-ice-to-disk/rmake.rs b/tests/run-make/dump-ice-to-disk/rmake.rs index a7a98d31f5046..09a34cdeb5e70 100644 --- a/tests/run-make/dump-ice-to-disk/rmake.rs +++ b/tests/run-make/dump-ice-to-disk/rmake.rs @@ -17,14 +17,14 @@ //! //! # Test history //! -//! - The previous rmake.rs iteration of this test was flakey for unknown reason on `i686-mingw` -//! *specifically*, so assertion failures in this test was made extremely verbose to help -//! diagnose why the ICE messages was different *specifically* on `i686-mingw`. -//! - An attempt is made to re-enable this test on `i686-mingw` (by removing `ignore-windows`). If -//! this test is still flakey, please restore the `ignore-windows` directive. - -//@ ignore-windows -//FIXME(#128911): still flakey on i686-mingw. +//! The previous rmake.rs iteration of this test was flaky for unknown reason on +//! `i686-pc-windows-gnu` *specifically*, so assertion failures in this test was made extremely +//! verbose to help diagnose why the ICE messages was different. It appears that backtraces on +//! `i686-pc-windows-gnu` specifically are quite unpredictable in how many backtrace frames are +//! involved. + +//@ ignore-cross-compile (exercising ICE dump on host) +//@ ignore-i686-pc-windows-gnu (unwind mechanism produces unpredictable backtraces) use std::cell::OnceCell; use std::path::{Path, PathBuf}; diff --git a/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs b/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs index a7215ca9d50bc..862b2bd5300c3 100644 --- a/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs +++ b/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs @@ -7,12 +7,11 @@ //! //! # Test history //! -//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming -//! those will be present in this test as well on the same platform +//! - Forked from `dump-ice-to-disk` test, which previously had backtrace unpredictability on +//! `i686-pc-windows-gnu`. -//@ needs-target-std -//@ ignore-windows -//FIXME(#128911): still flakey on i686-mingw. +//@ ignore-cross-compile (exercises metrics incremental on host) +//@ ignore-i686-pc-windows-gnu (unwind mechanism produces unpredictable backtraces) use std::path::{Path, PathBuf}; @@ -87,9 +86,7 @@ fn test_metrics_errors() { .env("RUST_BACKTRACE", "short") .arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist") .run_fail() - .assert_stderr_contains( - "error: cannot dump feature usage metrics: No such file or directory", - ) + .assert_stderr_contains("error: cannot dump feature usage metrics") .assert_stdout_not_contains("internal compiler error"); }); } diff --git a/tests/run-make/unstable-feature-usage-metrics/rmake.rs b/tests/run-make/unstable-feature-usage-metrics/rmake.rs index dbe078bf468a5..f987829741c72 100644 --- a/tests/run-make/unstable-feature-usage-metrics/rmake.rs +++ b/tests/run-make/unstable-feature-usage-metrics/rmake.rs @@ -7,12 +7,10 @@ //! //! # Test history //! -//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming -//! those will be present in this test as well on the same platform +//! - Forked from `dump-ice-to-disk` test, where `i686-pc-windows-gnu` has unpredictable backtraces. -//@ needs-target-std -//@ ignore-windows -//FIXME(#128911): still flakey on i686-mingw. +//@ ignore-cross-compile (exercises metrics dump on host) +//@ ignore-i686-pc-windows-gnu (unwind mechanism produces unpredictable backtraces) use std::path::{Path, PathBuf}; @@ -85,9 +83,7 @@ fn test_metrics_errors() { .env("RUST_BACKTRACE", "short") .arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist") .run_fail() - .assert_stderr_contains( - "error: cannot dump feature usage metrics: No such file or directory", - ) + .assert_stderr_contains("error: cannot dump feature usage metrics") .assert_stdout_not_contains("internal compiler error"); }); } diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index f5cfa9e0bccff..8bca20852add4 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -187,9 +187,9 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { struct RemoveParens; impl MutVisitor for RemoveParens { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { match e.kind.clone() { - ExprKind::Paren(inner) => *e = inner, + ExprKind::Paren(inner) => *e = *inner, _ => {} }; mut_visit::walk_expr(self, e); @@ -200,11 +200,11 @@ impl MutVisitor for RemoveParens { struct AddParens; impl MutVisitor for AddParens { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { mut_visit::walk_expr(self, e); let expr = std::mem::replace(e, Expr::dummy()); - e.kind = ExprKind::Paren(expr); + e.kind = ExprKind::Paren(P(expr)); } } diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 08bed40abe86b..90e07bed40e75 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -43,7 +43,6 @@ use std::process::ExitCode; use parser::parse_expr; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::ptr::P; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; @@ -157,7 +156,7 @@ static EXPRS: &[&str] = &[ struct Unparenthesize; impl MutVisitor for Unparenthesize { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { while let ExprKind::Paren(paren) = &mut e.kind { *e = mem::replace(paren, Expr::dummy()); } diff --git a/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr b/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr new file mode 100644 index 0000000000000..f54b6803b346a --- /dev/null +++ b/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `NotAValidResultType: VisitorResult` is not satisfied + --> $DIR/rustc-dev-remap.rs:LL:COL + | +LL | type Result = NotAValidResultType; + | ^^^^^^^^^^^^^^^^^^^ the trait `VisitorResult` is not implemented for `NotAValidResultType` + | + = help: the following other types implement trait `VisitorResult`: + () + ControlFlow +note: required by a bound in `rustc_ast::visit::Visitor::Result` + --> /rustc-dev/xyz/compiler/rustc_ast/src/visit.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr b/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr new file mode 100644 index 0000000000000..438c23458e2f5 --- /dev/null +++ b/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `NotAValidResultType: VisitorResult` is not satisfied + --> $DIR/rustc-dev-remap.rs:LL:COL + | +LL | type Result = NotAValidResultType; + | ^^^^^^^^^^^^^^^^^^^ the trait `VisitorResult` is not implemented for `NotAValidResultType` + | + = help: the following other types implement trait `VisitorResult`: + () + ControlFlow +note: required by a bound in `rustc_ast::visit::Visitor::Result` + --> $COMPILER_DIR_REAL/rustc_ast/src/visit.rs:LL:COL + | +LL | type Result: VisitorResult = (); + | ^^^^^^^^^^^^^ required by this bound in `Visitor::Result` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/rustc-dev-remap.rs b/tests/ui-fulldeps/rustc-dev-remap.rs new file mode 100644 index 0000000000000..aae7d4c0c90d1 --- /dev/null +++ b/tests/ui-fulldeps/rustc-dev-remap.rs @@ -0,0 +1,30 @@ +//@ check-fail +// +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +// +//@ revisions: only-remap remap-unremap +//@ compile-flags: -Z simulate-remapped-rust-src-base=/rustc-dev/xyz +//@ [remap-unremap]compile-flags: -Ztranslate-remapped-path-to-local-path=yes + +// The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically +// as the remapped revision will begin with $COMPILER_DIR_REAL, +// so we have to do it ourselves. +//@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:COL" + +#![feature(rustc_private)] + +extern crate rustc_ast; + +use rustc_ast::visit::Visitor; + +struct MyStruct; +struct NotAValidResultType; + +impl Visitor<'_> for MyStruct { + type Result = NotAValidResultType; + //~^ ERROR the trait bound `NotAValidResultType: VisitorResult` is not satisfied +} + +fn main() {} diff --git a/tests/ui/borrowck/span-semicolon-issue-139049.fixed b/tests/ui/borrowck/span-semicolon-issue-139049.fixed index 0b263b222963f..c01d1242dd73f 100644 --- a/tests/ui/borrowck/span-semicolon-issue-139049.fixed +++ b/tests/ui/borrowck/span-semicolon-issue-139049.fixed @@ -1,52 +1,25 @@ -// Make sure the generated suggestion suggest editing the user -// code instead of the std macro implementation +// Make sure the generated suggestion suggest editing the user code instead of +// the macro implementation (which might come from an external crate). +// issue: //@ run-rustfix #![allow(dead_code)] -use std::fmt::{self, Display}; - -struct Mutex; - -impl Mutex { - fn lock(&self) -> MutexGuard<'_> { - MutexGuard(self) - } -} - -struct MutexGuard<'a>(&'a Mutex); - -impl<'a> Drop for MutexGuard<'a> { - fn drop(&mut self) {} -} - -struct Out; - -impl Out { - fn write_fmt(&self, _args: fmt::Arguments) {} -} - -impl<'a> Display for MutexGuard<'a> { - fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { - Ok(()) - } -} +// You could assume that this comes from an extern crate (it doesn't +// because an aux crate would be overkill for this test). +macro_rules! perform { ($e:expr) => { D(&$e).end() } } +//~^ ERROR does not live long enough +//~| ERROR does not live long enough fn main() { - let _write = { - let mutex = Mutex; - write!(Out, "{}", mutex.lock()); - //~^ ERROR `mutex` does not live long enough - //~| SUGGESTION ; - }; - - let _write = { - use std::io::Write as _; + { let l = (); perform!(l); }; + //~^ SUGGESTION ; - let mutex = Mutex; - let x = write!(std::io::stdout(), "{}", mutex.lock()); x - //~^ ERROR `mutex` does not live long enough - //~| SUGGESTION let x - }; + let _x = { let l = (); let x = perform!(l); x }; + //~^ SUGGESTION let x } + +struct D(T); +impl Drop for D { fn drop(&mut self) {} } +impl D { fn end(&self) -> String { String::new() } } diff --git a/tests/ui/borrowck/span-semicolon-issue-139049.rs b/tests/ui/borrowck/span-semicolon-issue-139049.rs index a92742ac94b2c..43558756c718d 100644 --- a/tests/ui/borrowck/span-semicolon-issue-139049.rs +++ b/tests/ui/borrowck/span-semicolon-issue-139049.rs @@ -1,52 +1,25 @@ -// Make sure the generated suggestion suggest editing the user -// code instead of the std macro implementation +// Make sure the generated suggestion suggest editing the user code instead of +// the macro implementation (which might come from an external crate). +// issue: //@ run-rustfix #![allow(dead_code)] -use std::fmt::{self, Display}; - -struct Mutex; - -impl Mutex { - fn lock(&self) -> MutexGuard<'_> { - MutexGuard(self) - } -} - -struct MutexGuard<'a>(&'a Mutex); - -impl<'a> Drop for MutexGuard<'a> { - fn drop(&mut self) {} -} - -struct Out; - -impl Out { - fn write_fmt(&self, _args: fmt::Arguments) {} -} - -impl<'a> Display for MutexGuard<'a> { - fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { - Ok(()) - } -} +// You could assume that this comes from an extern crate (it doesn't +// because an aux crate would be overkill for this test). +macro_rules! perform { ($e:expr) => { D(&$e).end() } } +//~^ ERROR does not live long enough +//~| ERROR does not live long enough fn main() { - let _write = { - let mutex = Mutex; - write!(Out, "{}", mutex.lock()) - //~^ ERROR `mutex` does not live long enough - //~| SUGGESTION ; - }; - - let _write = { - use std::io::Write as _; + { let l = (); perform!(l) }; + //~^ SUGGESTION ; - let mutex = Mutex; - write!(std::io::stdout(), "{}", mutex.lock()) - //~^ ERROR `mutex` does not live long enough - //~| SUGGESTION let x - }; + let _x = { let l = (); perform!(l) }; + //~^ SUGGESTION let x } + +struct D(T); +impl Drop for D { fn drop(&mut self) {} } +impl D { fn end(&self) -> String { String::new() } } diff --git a/tests/ui/borrowck/span-semicolon-issue-139049.stderr b/tests/ui/borrowck/span-semicolon-issue-139049.stderr index 123bdf4bc67e4..8d2de67382bd8 100644 --- a/tests/ui/borrowck/span-semicolon-issue-139049.stderr +++ b/tests/ui/borrowck/span-semicolon-issue-139049.stderr @@ -1,46 +1,48 @@ -error[E0597]: `mutex` does not live long enough - --> $DIR/span-semicolon-issue-139049.rs:39:27 +error[E0597]: `l` does not live long enough + --> $DIR/span-semicolon-issue-139049.rs:11:41 | -LL | let mutex = Mutex; - | ----- binding `mutex` declared here -LL | write!(Out, "{}", mutex.lock()) - | ^^^^^------- - | | - | borrowed value does not live long enough - | a temporary with access to the borrow is created here ... +LL | macro_rules! perform { ($e:expr) => { D(&$e).end() } } + | --^^^- + | | | + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... ... -LL | }; - | -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard` - | | - | `mutex` dropped here while still borrowed +LL | { let l = (); perform!(l) }; + | - ----------- -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `l` dropped here while still borrowed + | | in this macro invocation + | binding `l` declared here | + = note: this error originates in the macro `perform` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped | -LL | write!(Out, "{}", mutex.lock()); - | + +LL | { let l = (); perform!(l); }; + | + -error[E0597]: `mutex` does not live long enough - --> $DIR/span-semicolon-issue-139049.rs:48:41 +error[E0597]: `l` does not live long enough + --> $DIR/span-semicolon-issue-139049.rs:11:41 | -LL | let mutex = Mutex; - | ----- binding `mutex` declared here -LL | write!(std::io::stdout(), "{}", mutex.lock()) - | ^^^^^------- - | | - | borrowed value does not live long enough - | a temporary with access to the borrow is created here ... +LL | macro_rules! perform { ($e:expr) => { D(&$e).end() } } + | --^^^- + | | | + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... ... -LL | }; - | -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard` - | | - | `mutex` dropped here while still borrowed +LL | let _x = { let l = (); perform!(l) }; + | - ----------- -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `l` dropped here while still borrowed + | | in this macro invocation + | binding `l` declared here | = note: the temporary is part of an expression at the end of a block; consider forcing this temporary to be dropped sooner, before the block's local variables are dropped + = note: this error originates in the macro `perform` (in Nightly builds, run with -Z macro-backtrace for more info) help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block | -LL | let x = write!(std::io::stdout(), "{}", mutex.lock()); x - | +++++++ +++ +LL | let _x = { let l = (); let x = perform!(l); x }; + | +++++++ +++ error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.rs b/tests/ui/feature-gates/feature-gate-default-field-values.rs index d2e41a7160259..4631f51b9d840 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.rs +++ b/tests/ui/feature-gates/feature-gate-default-field-values.rs @@ -58,6 +58,16 @@ pub enum OptEnum { } } +// Default field values may not be used on `union`s (at least, this is not described in the accepted +// RFC, and it's not currently clear how to extend the design to do so). We emit a feature gate +// error when the feature is not enabled, but syntactically reject default field values when used +// with unions when the feature is enabled. This can be adjusted if there's an acceptable design +// extension, or just unconditionally reject always. +union U { + x: i32 = 0, //~ ERROR default values on fields are experimental + y: f32 = 0.0, //~ ERROR default values on fields are experimental +} + fn main () { let x = Foo { .. }; //~ ERROR base expression required after `..` let y = Foo::default(); diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.stderr b/tests/ui/feature-gates/feature-gate-default-field-values.stderr index 104d72a39861d..292c38990726e 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.stderr +++ b/tests/ui/feature-gates/feature-gate-default-field-values.stderr @@ -124,8 +124,28 @@ LL | optional: () = (), = help: add `#![feature(default_field_values)]` 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]: default values on fields are experimental + --> $DIR/feature-gate-default-field-values.rs:67:11 + | +LL | x: i32 = 0, + | ^^^^ + | + = note: see issue #132162 for more information + = help: add `#![feature(default_field_values)]` 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]: default values on fields are experimental + --> $DIR/feature-gate-default-field-values.rs:68:11 + | +LL | y: f32 = 0.0, + | ^^^^^^ + | + = note: see issue #132162 for more information + = help: add `#![feature(default_field_values)]` 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[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:62:21 + --> $DIR/feature-gate-default-field-values.rs:72:21 | LL | let x = Foo { .. }; | ^ @@ -140,7 +160,7 @@ LL | let x = Foo { ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:64:29 + --> $DIR/feature-gate-default-field-values.rs:74:29 | LL | let z = Foo { baz: 1, .. }; | ^ @@ -155,7 +175,7 @@ LL | let z = Foo { baz: 1, ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:70:26 + --> $DIR/feature-gate-default-field-values.rs:80:26 | LL | let x = Bar::Foo { .. }; | ^ @@ -170,7 +190,7 @@ LL | let x = Bar::Foo { ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:72:34 + --> $DIR/feature-gate-default-field-values.rs:82:34 | LL | let z = Bar::Foo { baz: 1, .. }; | ^ @@ -185,7 +205,7 @@ LL | let z = Bar::Foo { baz: 1, ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:78:31 + --> $DIR/feature-gate-default-field-values.rs:88:31 | LL | let x = Qux:: { .. }; | ^ @@ -200,7 +220,7 @@ LL | let x = Qux:: { ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:79:73 + --> $DIR/feature-gate-default-field-values.rs:89:73 | LL | assert!(matches!(Qux:: { bar: S, baz: 42, bat: 2, bay: 4, .. }, x)); | ^ @@ -215,7 +235,7 @@ LL | assert!(matches!(Qux:: { bar: S, baz: 42, bat: 2, bay: 4, ../* | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:82:38 + --> $DIR/feature-gate-default-field-values.rs:92:38 | LL | let y = Opt { mandatory: None, .. }; | ^ @@ -230,7 +250,7 @@ LL | let y = Opt { mandatory: None, ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:86:47 + --> $DIR/feature-gate-default-field-values.rs:96:47 | LL | assert!(matches!(Opt { mandatory: None, .. }, z)); | ^ @@ -245,7 +265,7 @@ LL | assert!(matches!(Opt { mandatory: None, ../* expr */ }, z)); | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:88:30 + --> $DIR/feature-gate-default-field-values.rs:98:30 | LL | assert!(matches!(Opt { .. }, z)); | ^ @@ -256,7 +276,7 @@ LL | assert!(matches!(Opt { ../* expr */ }, z)); | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:90:44 + --> $DIR/feature-gate-default-field-values.rs:100:44 | LL | assert!(matches!(Opt { optional: (), .. }, z)); | ^ @@ -267,7 +287,7 @@ LL | assert!(matches!(Opt { optional: (), ../* expr */ }, z)); | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:92:61 + --> $DIR/feature-gate-default-field-values.rs:102:61 | LL | assert!(matches!(Opt { optional: (), mandatory: None, .. }, z)); | ^ @@ -279,7 +299,7 @@ LL + assert!(matches!(Opt { optional: (), mandatory: None, }, z)); | error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:94:51 + --> $DIR/feature-gate-default-field-values.rs:104:51 | LL | let y = OptEnum::Variant { mandatory: None, .. }; | ^ @@ -294,7 +314,7 @@ LL | let y = OptEnum::Variant { mandatory: None, ../* expr */ }; | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:98:60 + --> $DIR/feature-gate-default-field-values.rs:108:60 | LL | assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z)); | ^ @@ -309,7 +329,7 @@ LL | assert!(matches!(OptEnum::Variant { mandatory: None, ../* expr */ }, z) | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:100:43 + --> $DIR/feature-gate-default-field-values.rs:110:43 | LL | assert!(matches!(OptEnum::Variant { .. }, z)); | ^ @@ -320,7 +340,7 @@ LL | assert!(matches!(OptEnum::Variant { ../* expr */ }, z)); | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:102:57 + --> $DIR/feature-gate-default-field-values.rs:112:57 | LL | assert!(matches!(OptEnum::Variant { optional: (), .. }, z)); | ^ @@ -331,7 +351,7 @@ LL | assert!(matches!(OptEnum::Variant { optional: (), ../* expr */ }, z)); | ++++++++++ error[E0797]: base expression required after `..` - --> $DIR/feature-gate-default-field-values.rs:104:74 + --> $DIR/feature-gate-default-field-values.rs:114:74 | LL | assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. }, z)); | ^ @@ -342,7 +362,7 @@ LL - assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, .. } LL + assert!(matches!(OptEnum::Variant { optional: (), mandatory: None, }, z)); | -error: aborting due to 29 previous errors +error: aborting due to 31 previous errors Some errors have detailed explanations: E0658, E0797. For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/structs/default-field-values/failures.rs b/tests/ui/structs/default-field-values/failures.rs index dee6566bd0ea1..9c5b7172929c6 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -49,6 +49,12 @@ enum E { Variant {} //~ ERROR the `#[default]` attribute may only be used on unit enum variants } +union U +{ + x: i32 = 1, //~ ERROR unions cannot have default field values + y: f32 = 2., //~ ERROR unions cannot have default field values +} + fn main () { let _ = Foo { .. }; // ok let _ = Foo::default(); // ok diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index aaa75fd3180c5..5e3d4c89c2a3c 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -12,6 +12,18 @@ error: default fields are not supported in tuple structs LL | pub struct Rak(i32 = 42); | ^^ default fields are only supported on structs +error: unions cannot have default field values + --> $DIR/failures.rs:54:14 + | +LL | x: i32 = 1, + | ^ + +error: unions cannot have default field values + --> $DIR/failures.rs:55:14 + | +LL | y: f32 = 2., + | ^^ + error[E0277]: the trait bound `S: Default` is not satisfied --> $DIR/failures.rs:16:5 | @@ -28,19 +40,19 @@ LL | pub struct S; | error: missing field `bar` in initializer - --> $DIR/failures.rs:55:19 + --> $DIR/failures.rs:61:19 | LL | let _ = Bar { .. }; | ^ fields that do not have a defaulted value must be provided explicitly error: missing field `bar` in initializer - --> $DIR/failures.rs:56:27 + --> $DIR/failures.rs:62:27 | LL | let _ = Bar { baz: 0, .. }; | ^ fields that do not have a defaulted value must be provided explicitly error[E0308]: mismatched types - --> $DIR/failures.rs:60:17 + --> $DIR/failures.rs:66:17 | LL | let _ = Rak(..); | --- ^^ expected `i32`, found `RangeFull` @@ -53,19 +65,19 @@ note: tuple struct defined here LL | pub struct Rak(i32 = 42); | ^^^ help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/failures.rs:60:17 + --> $DIR/failures.rs:66:17 | LL | let _ = Rak(..); | ^^ error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/failures.rs:62:13 + --> $DIR/failures.rs:68:13 | LL | let _ = Rak(0, ..); | ^^^ -- unexpected argument #2 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/failures.rs:62:20 + --> $DIR/failures.rs:68:20 | LL | let _ = Rak(0, ..); | ^^ @@ -81,13 +93,13 @@ LL + let _ = Rak(0); | error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/failures.rs:64:13 + --> $DIR/failures.rs:70:13 | LL | let _ = Rak(.., 0); | ^^^ -- unexpected argument #1 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/failures.rs:64:17 + --> $DIR/failures.rs:70:17 | LL | let _ = Rak(.., 0); | ^^ @@ -102,7 +114,7 @@ LL - let _ = Rak(.., 0); LL + let _ = Rak(0); | -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0061, E0277, E0308. For more information about an error, try `rustc --explain E0061`.