diff --git a/.gitmodules b/.gitmodules index c39fabac55f3b..9f9d573668245 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/22.1-2026-03-22 + branch = rustc/22.1-2026-05-19 shallow = true [submodule "src/doc/embedded-book"] path = src/doc/embedded-book diff --git a/.mailmap b/.mailmap index 4a3e39831e8a2..3fc567081c1ff 100644 --- a/.mailmap +++ b/.mailmap @@ -569,6 +569,7 @@ Philipp Matthias Schäfer phosphorus Pierre Krieger pierwill <19642016+pierwill@users.noreply.github.com> +Pieter-Louis Schoeman <127837395+P8L1@users.noreply.github.com> Pietro Albini Pietro Albini Pradyumna Rahul diff --git a/Cargo.lock b/Cargo.lock index e16648dbef325..059b203fb06c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4010,6 +4010,7 @@ dependencies = [ name = "rustc_feature" version = "0.0.0" dependencies = [ + "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_span", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fa29fb76c51a8..af8ad425507c4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3582,6 +3582,13 @@ pub struct ImplRestriction { pub tokens: Option, } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct MutRestriction { + pub kind: RestrictionKind, + pub span: Span, + pub tokens: Option, +} + #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum RestrictionKind { Unrestricted, @@ -3597,6 +3604,7 @@ pub struct FieldDef { pub id: NodeId, pub span: Span, pub vis: Visibility, + pub mut_restriction: MutRestriction, pub safety: Safety, pub ident: Option, diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 73bfa0ba7ab6d..74d8ae469b3d1 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -8,8 +8,8 @@ use std::marker::PhantomData; use crate::tokenstream::LazyAttrTokenStream; use crate::{ Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField, - FieldDef, ForeignItem, GenericParam, ImplRestriction, Item, NodeId, Param, Pat, PatField, Path, - Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate, + FieldDef, ForeignItem, GenericParam, ImplRestriction, Item, MutRestriction, NodeId, Param, Pat, + PatField, Path, Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate, }; /// A trait for AST nodes having an ID. @@ -108,7 +108,8 @@ impl_has_tokens!( Path, Ty, Visibility, - ImplRestriction + ImplRestriction, + MutRestriction, ); impl_has_tokens_none!( Arm, @@ -254,7 +255,17 @@ impl_has_attrs!( Variant, WherePredicate, ); -impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility, ImplRestriction); +impl_has_attrs_none!( + Attribute, + AttrItem, + Block, + Pat, + Path, + Ty, + Visibility, + ImplRestriction, + MutRestriction +); impl HasAttrs for Box { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ed8c3787bfec4..1e96d1d52f7eb 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -582,12 +582,14 @@ macro_rules! common_visitor_and_walkers { fn visit_generics(Generics); fn visit_inline_asm(InlineAsm); fn visit_inline_asm_sym(InlineAsmSym); + fn visit_impl_restriction(ImplRestriction); //fn visit_item(Item); fn visit_label(Label); fn visit_lifetime(Lifetime, _ctxt: LifetimeCtxt); fn visit_local(Local); fn visit_mac_call(MacCall); fn visit_macro_def(MacroDef); + fn visit_mut_restriction(MutRestriction); fn visit_param_bound(GenericBound, _ctxt: BoundKind); fn visit_param(Param); fn visit_pat_field(PatField); @@ -597,7 +599,6 @@ macro_rules! common_visitor_and_walkers { fn visit_poly_trait_ref(PolyTraitRef); fn visit_precise_capturing_arg(PreciseCapturingArg); fn visit_qself(QSelf); - fn visit_impl_restriction(ImplRestriction); fn visit_trait_ref(TraitRef); fn visit_ty_pat(TyPat); fn visit_ty(Ty); @@ -1106,12 +1107,14 @@ macro_rules! common_visitor_and_walkers { pub fn walk_generics(Generics); pub fn walk_inline_asm(InlineAsm); pub fn walk_inline_asm_sym(InlineAsmSym); + pub fn walk_impl_restriction(ImplRestriction); //pub fn walk_item(Item); pub fn walk_label(Label); pub fn walk_lifetime(Lifetime); pub fn walk_local(Local); pub fn walk_mac(MacCall); pub fn walk_macro_def(MacroDef); + pub fn walk_mut_restriction(MutRestriction); pub fn walk_param_bound(GenericBound); pub fn walk_param(Param); pub fn walk_pat_field(PatField); @@ -1121,7 +1124,6 @@ macro_rules! common_visitor_and_walkers { pub fn walk_poly_trait_ref(PolyTraitRef); pub fn walk_precise_capturing_arg(PreciseCapturingArg); pub fn walk_qself(QSelf); - pub fn walk_impl_restriction(ImplRestriction); pub fn walk_trait_ref(TraitRef); pub fn walk_ty_pat(TyPat); pub fn walk_ty(Ty); diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index df5ea7c3168ff..8accaa8c3125c 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -58,8 +58,8 @@ use smallvec::SmallVec; use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee}; use crate::{ - AllowReturnTypeNotation, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, - ParamMode, ResolverAstLoweringExt, + AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, + ResolverAstLoweringExt, }; mod generics; @@ -144,7 +144,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut generics = self.uplift_delegation_generics(delegation, sig_id, is_method); - let body_id = self.lower_delegation_body( + let (body_id, call_expr_id) = self.lower_delegation_body( delegation, is_method, param_count, @@ -152,16 +152,23 @@ impl<'hir> LoweringContext<'_, 'hir> { span, ); - let decl = - self.lower_delegation_decl(sig_id, param_count, c_variadic, span, &generics); + let decl = self.lower_delegation_decl( + sig_id, + param_count, + c_variadic, + span, + &generics, + delegation.id, + call_expr_id, + ); let sig = self.lower_delegation_sig(sig_id, decl, span); let ident = self.lower_ident(delegation.ident); let generics = self.arena.alloc(hir::Generics { has_where_clause_predicates: false, - params: self.arena.alloc_from_iter(generics.all_params(span, self)), - predicates: self.arena.alloc_from_iter(generics.all_predicates(span, self)), + params: self.arena.alloc_from_iter(generics.all_params()), + predicates: self.arena.alloc_from_iter(generics.all_predicates()), span, where_clause_span: span, }); @@ -280,6 +287,8 @@ impl<'hir> LoweringContext<'_, 'hir> { c_variadic: bool, span: Span, generics: &GenericsGenerationResults<'hir>, + call_path_node_id: NodeId, + call_expr_id: HirId, ) -> &'hir hir::FnDecl<'hir> { // The last parameter in C variadic functions is skipped in the signature, // like during regular lowering. @@ -297,7 +306,9 @@ impl<'hir> LoweringContext<'_, 'hir> { hir_id: self.next_id(), kind: hir::TyKind::InferDelegation(hir::InferDelegation::Sig( sig_id, - hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationGenerics { + hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationInfo { + call_expr_id, + call_path_res: self.get_resolution_id(call_path_node_id), child_args_segment_id: generics.child.args_segment_id, parent_args_segment_id: generics.parent.args_segment_id, self_ty_id: generics.self_ty_id, @@ -400,10 +411,11 @@ impl<'hir> LoweringContext<'_, 'hir> { param_count: usize, generics: &mut GenericsGenerationResults<'hir>, span: Span, - ) -> BodyId { + ) -> (BodyId, HirId) { let block = delegation.body.as_deref(); + let mut call_expr_id = HirId::INVALID; - self.lower_body(|this| { + let block_id = self.lower_body(|this| { let mut parameters: Vec> = Vec::with_capacity(param_count); let mut args: Vec> = Vec::with_capacity(param_count); @@ -440,10 +452,17 @@ impl<'hir> LoweringContext<'_, 'hir> { args.push(this.lower_target_expr(&block)); } - let final_expr = this.finalize_body_lowering(delegation, args, generics, span); + let (final_expr, hir_id) = + this.finalize_body_lowering(delegation, args, generics, span); + + call_expr_id = hir_id; (this.arena.alloc_from_iter(parameters), final_expr) - }) + }); + + debug_assert_ne!(call_expr_id, HirId::INVALID); + + (block_id, call_expr_id) } // FIXME(fn_delegation): Alternatives for target expression lowering: @@ -459,108 +478,59 @@ impl<'hir> LoweringContext<'_, 'hir> { self.mk_expr(hir::ExprKind::Block(block, None), block.span) } - // Generates expression for the resulting body. If possible, `MethodCall` is used - // to allow autoref/autoderef for target expression. For example in: - // - // trait Trait : Sized { - // fn by_value(self) -> i32 { 1 } - // fn by_mut_ref(&mut self) -> i32 { 2 } - // fn by_ref(&self) -> i32 { 3 } - // } - // - // struct NewType(SomeType); - // impl Trait for NewType { - // reuse Trait::* { self.0 } - // } - // - // `self.0` will automatically coerce. fn finalize_body_lowering( &mut self, delegation: &Delegation, args: Vec>, generics: &mut GenericsGenerationResults<'hir>, span: Span, - ) -> hir::Expr<'hir> { - let args = self.arena.alloc_from_iter(args); - - let has_generic_args = - delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some()); - - let call = if self - .get_resolution_id(delegation.id) - .map(|def_id| self.is_method(def_id, span)) - .unwrap_or_default() - && delegation.qself.is_none() - && !has_generic_args - && !args.is_empty() - { - let ast_segment = delegation.path.segments.last().unwrap(); - let segment = self.lower_path_segment( - delegation.path.span, - ast_segment, - ParamMode::Optional, - GenericArgsMode::Err, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - - // FIXME(fn_delegation): proper support for parent generics propagation - // in method call scenario. - let segment = self.process_segment(span, &segment, &mut generics.child); - let segment = self.arena.alloc(segment); - - self.arena.alloc(hir::Expr { - hir_id: self.next_id(), - kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span), - span, - }) - } else { - let path = self.lower_qpath( - delegation.id, - &delegation.qself, - &delegation.path, - ParamMode::Optional, - AllowReturnTypeNotation::No, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - - let new_path = match path { - hir::QPath::Resolved(ty, path) => { - let mut new_path = path.clone(); - let len = new_path.segments.len(); - - new_path.segments = self.arena.alloc_from_iter( - new_path.segments.iter().enumerate().map(|(idx, segment)| { - if idx + 2 == len { - self.process_segment(span, segment, &mut generics.parent) - } else if idx + 1 == len { - self.process_segment(span, segment, &mut generics.child) - } else { - segment.clone() - } - }), - ); - - hir::QPath::Resolved(ty, self.arena.alloc(new_path)) - } - hir::QPath::TypeRelative(ty, segment) => { - let segment = self.process_segment(span, segment, &mut generics.child); - - hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) - } - }; + ) -> (hir::Expr<'hir>, HirId) { + let path = self.lower_qpath( + delegation.id, + &delegation.qself, + &delegation.path, + ParamMode::Optional, + AllowReturnTypeNotation::No, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + + let new_path = match path { + hir::QPath::Resolved(ty, path) => { + let mut new_path = path.clone(); + let len = new_path.segments.len(); + + new_path.segments = self.arena.alloc_from_iter( + new_path.segments.iter().enumerate().map(|(idx, segment)| { + if idx + 2 == len { + self.process_segment(span, segment, &mut generics.parent) + } else if idx + 1 == len { + self.process_segment(span, segment, &mut generics.child) + } else { + segment.clone() + } + }), + ); - generics.self_ty_id = match new_path { - hir::QPath::Resolved(ty, _) => ty, - hir::QPath::TypeRelative(ty, _) => Some(ty), + hir::QPath::Resolved(ty, self.arena.alloc(new_path)) } - .map(|ty| ty.hir_id); + hir::QPath::TypeRelative(ty, segment) => { + let segment = self.process_segment(span, segment, &mut generics.child); - let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span)); - self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)) + hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) + } }; + generics.self_ty_id = match new_path { + hir::QPath::Resolved(ty, _) => ty, + hir::QPath::TypeRelative(ty, _) => Some(ty), + } + .map(|ty| ty.hir_id); + + let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span)); + let args = self.arena.alloc_from_iter(args); + let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)); + let block = self.arena.alloc(hir::Block { stmts: &[], expr: Some(call), @@ -570,7 +540,7 @@ impl<'hir> LoweringContext<'_, 'hir> { targeted_by_break: false, }); - self.mk_expr(hir::ExprKind::Block(block, None), span) + (self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id) } fn process_segment( @@ -581,8 +551,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::PathSegment<'hir> { let details = result.generics.args_propagation_details(); + // Always uplift generic params, because if they are not empty then they + // should be generated in delegation. + let generics = result.generics.into_hir_generics(self, span); let segment = if details.should_propagate { - let generics = result.generics.into_hir_generics(self, span); let args = generics.into_generic_args(self, span); // Needed for better error messages (`trait-impl-wrong-args-count.rs` test). diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index c2c3bd740e3cf..f8e3528750035 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -171,22 +171,9 @@ impl<'hir> GenericsGenerationResult<'hir> { } impl<'hir> GenericsGenerationResults<'hir> { - pub(super) fn all_params( - &mut self, - span: Span, - ctx: &mut LoweringContext<'_, 'hir>, - ) -> impl Iterator> { - // Now we always call `into_hir_generics` both on child and parent, - // however in future we would not do that, when scenarios like - // method call will be supported (if HIR generics were not obtained - // then it means that we did not propagated them, thus we do not need - // to generate params). - let mut create_params = |result: &mut GenericsGenerationResult<'hir>| { - result.generics.into_hir_generics(ctx, span).hir_generics_or_empty().params - }; - - let parent = create_params(&mut self.parent); - let child = create_params(&mut self.child); + pub(super) fn all_params(&self) -> impl Iterator> { + let parent = self.parent.generics.hir_generics_or_empty().params; + let child = self.child.generics.hir_generics_or_empty().params; // Order generics, first we have parent and child lifetimes, // then parent and child types and consts. @@ -205,24 +192,14 @@ impl<'hir> GenericsGenerationResults<'hir> { /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics. /// Those predicates will not affect resulting predicate inheritance and folding /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature. - pub(super) fn all_predicates( - &mut self, - span: Span, - ctx: &mut LoweringContext<'_, 'hir>, - ) -> impl Iterator> { - // Now we always call `into_hir_generics` both on child and parent, - // however in future we would not do that, when scenarios like - // method call will be supported (if HIR generics were not obtained - // then it means that we did not propagated them, thus we do not need - // to generate predicates). - let mut create_predicates = |result: &mut GenericsGenerationResult<'hir>| { - result.generics.into_hir_generics(ctx, span).hir_generics_or_empty().predicates - }; - - let parent = create_predicates(&mut self.parent); - let child = create_predicates(&mut self.child); - - parent.into_iter().chain(child).copied() + pub(super) fn all_predicates(&self) -> impl Iterator> { + self.parent + .generics + .hir_generics_or_empty() + .predicates + .into_iter() + .chain(self.child.generics.hir_generics_or_empty().predicates) + .copied() } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 3da133f5e1136..a916ee1f143bd 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1197,8 +1197,6 @@ impl<'hir> LoweringContext<'_, 'hir> { } } else { self.emit_bad_parenthesized_trait_in_assoc_ty(data); - // FIXME(return_type_notation): we could issue a feature error - // if the parens are empty and there's no return type. self.lower_angle_bracketed_parameter_data( &data.as_angle_bracketed_args(), ParamMode::Explicit, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b43dbad611a0e..1b3ce2427c3c7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -505,6 +505,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(move_expr, "`move(expr)` syntax is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); + gate_all!(mut_restriction, "`mut` restrictions are experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(return_type_notation, "return type notation is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 25b398b849246..19bd8fd11bf2e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -84,6 +84,10 @@ pub fn impl_restriction_to_string(r: &ast::ImplRestriction) -> String { State::new().impl_restriction_to_string(r) } +pub fn mut_restriction_to_string(r: &ast::MutRestriction) -> String { + State::new().mut_restriction_to_string(r) +} + pub fn meta_list_item_to_string(li: &ast::MetaItemInner) -> String { State::new().meta_list_item_to_string(li) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f46ce8fd76865..8b2da2acaa520 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1153,6 +1153,10 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere Self::to_string(|s| s.print_impl_restriction(r)) } + fn mut_restriction_to_string(&self, r: &ast::MutRestriction) -> String { + Self::to_string(|s| s.print_mut_restriction(r)) + } + fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { let (cb, ib) = s.head(""); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index b3a8f5d8cac30..820c49cd0612c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -511,6 +511,20 @@ impl<'a> State<'a> { } } + pub(crate) fn print_mut_restriction(&mut self, mut_restriction: &ast::MutRestriction) { + match &mut_restriction.kind { + ast::RestrictionKind::Restricted { path, shorthand, .. } => { + let path = Self::to_string(|s| s.print_path(path, false, 0)); + if *shorthand { + self.word_nbsp(format!("mut({path})")) + } else { + self.word_nbsp(format!("mut(in {path})")) + } + } + ast::RestrictionKind::Unrestricted => {} + } + } + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { if let ast::Defaultness::Default(_) = defaultness { self.word_nbsp("default"); @@ -537,6 +551,7 @@ impl<'a> State<'a> { s.maybe_print_comment(field.span.lo()); s.print_outer_attributes(&field.attrs); s.print_visibility(&field.vis); + s.print_mut_restriction(&field.mut_restriction); s.print_type(&field.ty) }); self.pclose(); @@ -562,6 +577,7 @@ impl<'a> State<'a> { self.maybe_print_comment(field.span.lo()); self.print_outer_attributes(&field.attrs); self.print_visibility(&field.vis); + self.print_mut_restriction(&field.mut_restriction); self.print_ident(field.ident.unwrap()); self.word_nbsp(":"); self.print_type(&field.ty); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index f838f73838499..4c5616084cb0b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -327,8 +327,11 @@ pub fn parse_cfg_attr( }) { Ok(r) => return Some(r), Err(e) => { - let suggestions = CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr); + let suggestions = CFG_ATTR_TEMPLATE.suggestions( + AttrSuggestionStyle::Attribute(cfg_attr.style), + cfg_attr.get_normal_item().unsafety, + sym::cfg_attr, + ); e.with_span_suggestions( cfg_attr.span, "must be of the form", @@ -360,8 +363,11 @@ pub fn parse_cfg_attr( description: ParsedDescription::Attribute, reason, suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( - CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr), + CFG_ATTR_TEMPLATE.suggestions( + AttrSuggestionStyle::Attribute(cfg_attr.style), + cfg_attr.get_normal_item().unsafety, + sym::cfg_attr, + ), ), }); } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 9b4ffc7ea27f6..c2dfc4a147ee3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -396,9 +396,7 @@ pub(crate) fn parse_format_string( .map(|piece| match piece { RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)), RpfPiece::NextArgument(arg) => { - warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); - let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal); - Piece::Arg(arg) + Piece::Arg(parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal)) } }) .collect(); @@ -415,15 +413,25 @@ fn parse_arg( ) -> FormatArg { let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); - match arg.position { + let mut check_format = true; + + let ret = match arg.position { // Something like "hello {name}" Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) { (Mode::RustcOnUnimplemented, sym::ItemContext) => FormatArg::ItemContext, - // Like `{This}`, but sugared. - // FIXME(mejrs) maybe rename/rework this or something - // if we want to apply this to other attrs? - (Mode::RustcOnUnimplemented, sym::Trait) => FormatArg::Trait, + // `{This:ty}` + (Mode::RustcOnUnimplemented, sym::This) => match arg.format.ty { + "resolved" => { + check_format = false; + FormatArg::ThisResolved + } + "path" => { + check_format = false; + FormatArg::ThisPath + } + _ => FormatArg::This, + }, // Some diagnostic attributes can use `{This}` to refer to the annotated item. // For those that don't, we continue and maybe use it as a generic parameter. @@ -431,10 +439,7 @@ fn parse_arg( // FIXME(mejrs) `DiagnosticOnUnimplemented` is intentionally not here; // that requires lang approval which is best kept for a standalone PR. ( - Mode::RustcOnUnimplemented - | Mode::DiagnosticOnUnknown - | Mode::DiagnosticOnMove - | Mode::DiagnosticOnUnmatchArgs, + Mode::DiagnosticOnUnknown | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnmatchArgs, sym::This, ) => FormatArg::This, @@ -471,11 +476,11 @@ fn parse_arg( attr: mode.as_str(), allowed: mode.allowed_format_arguments(), }); - return FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}"))); + FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}"))) } }, - // `{:1}` and `{}` are ignored + // `{1}` and `{}` are ignored Position::ArgumentIs(idx) => { warnings.push(FormatWarning::IndexedArgument { span }); FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}"))) @@ -484,7 +489,11 @@ fn parse_arg( warnings.push(FormatWarning::PositionalArgument { span }); FormatArg::AsIs(sym::empty_braces) } + }; + if check_format { + warn_on_format_spec(&arg.format, warnings, input_span, is_source_literal); } + ret } /// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything @@ -495,12 +504,8 @@ fn warn_on_format_spec( input_span: Span, is_source_literal: bool, ) { - if spec.ty != "" { - let span = spec - .ty_span - .as_ref() - .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) - .unwrap_or(input_span); + if let Some(ty_span) = &spec.ty_span { + let span = slice_span(input_span, ty_span.clone(), is_source_literal); warnings.push(FormatWarning::InvalidSpecifier { span }) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 81cd32faea127..750fe10a483fc 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,7 +5,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; -use rustc_ast::{AttrStyle, MetaItemLit}; +use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; @@ -357,6 +357,9 @@ pub struct AcceptContext<'f, 'sess> { /// Used in reporting errors to give a hint to users what the attribute *should* look like. pub(crate) template: &'f AttributeTemplate, + /// The safety attribute (if any) applied to the attribute. + pub(crate) attr_safety: Safety, + /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, } @@ -873,7 +876,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions(style, &self.attr_path) + self.template.suggestions(style, self.attr_safety, &self.attr_path) } } @@ -1064,7 +1067,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions(style, &self.attr_path) + self.template.suggestions(style, self.attr_safety, &self.attr_path) } /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 8260663850344..4a0ad63c0c053 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -234,6 +234,7 @@ impl<'sess> AttributeParser<'sess> { attr_style, parsed_description, template, + attr_safety: attr_safety.unwrap_or(Safety::Default), attr_path, }; parse_fn(&mut cx, args) @@ -404,6 +405,7 @@ impl<'sess> AttributeParser<'sess> { attr_style: attr.style, parsed_description: ParsedDescription::Attribute, template: &accept.template, + attr_safety: n.item.unsafety, attr_path: attr_path.clone(), }; diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index fcf4bf4a7b7a4..9a64a063fcf5c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -580,6 +580,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // for dbg!(x) which may take ownership, suggest dbg!(&x) instead + // but here we actually do not check whether the macro name is `dbg!` + // so that we may extend the scope a bit larger to cover more cases fn suggest_ref_for_dbg_args( &self, body: &hir::Expr<'_>, @@ -593,41 +595,29 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); let Some(var_info) = var_info else { return }; let arg_name = var_info.name; - struct MatchArgFinder<'tcx> { - tcx: TyCtxt<'tcx>, - move_span: Span, + struct MatchArgFinder { + expr_span: Span, + match_arg_span: Option, arg_name: Symbol, - match_arg_span: Option = None, } - impl Visitor<'_> for MatchArgFinder<'_> { + impl Visitor<'_> for MatchArgFinder { fn visit_expr(&mut self, e: &hir::Expr<'_>) { // dbg! is expanded into a match pattern, we need to find the right argument span - if let hir::ExprKind::Match(scrutinee, ..) = &e.kind - && let hir::ExprKind::Tup(args) = scrutinee.kind - && e.span.macro_backtrace().any(|expn| { - expn.macro_def_id.is_some_and(|macro_def_id| { - self.tcx.is_diagnostic_item(sym::dbg_macro, macro_def_id) - }) - }) + if let hir::ExprKind::Match(expr, ..) = &e.kind + && let hir::ExprKind::Path(hir::QPath::Resolved( + _, + path @ Path { segments: [seg], .. }, + )) = &expr.kind + && seg.ident.name == self.arg_name + && self.expr_span.source_callsite().contains(expr.span) { - for arg in args { - if let hir::ExprKind::Path(hir::QPath::Resolved( - _, - path @ Path { segments: [seg], .. }, - )) = &arg.kind - && seg.ident.name == self.arg_name - && self.move_span.source_equal(arg.span) - { - self.match_arg_span = Some(path.span); - return; - } - } + self.match_arg_span = Some(path.span); } hir::intravisit::walk_expr(self, e); } } - let mut finder = MatchArgFinder { tcx: self.infcx.tcx, move_span, arg_name, .. }; + let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name }; finder.visit_expr(body); if let Some(macro_arg_span) = finder.match_arg_span { err.span_suggestion_verbose( diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index b51604fb2903a..7106bb81da97b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -8,7 +8,7 @@ use tracing::debug; use crate::region_infer::RegionInferenceContext; impl<'tcx> RegionInferenceContext<'tcx> { - /// Find the the name and span of the variable corresponding to the given region. + /// Find the name and span of the variable corresponding to the given region. /// The returned var will also be ensured to actually be used in `body`. pub(crate) fn get_var_name_and_span_for_region( &self, diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index d779089eeaa4c..a1e14b5245137 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -10,7 +10,7 @@ use rustc_parse_format as parse; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; -use smallvec::{SmallVec, smallvec}; +use smallvec::smallvec; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; @@ -26,24 +26,6 @@ struct ValidatedAsmArgs { pub options_spans: Vec, } -struct MacGlobalAsm { - item: ast::Item, -} - -impl MacResult for MacGlobalAsm { - fn make_items(self: Box) -> Option; 1]>> { - Some(smallvec![Box::new(self.item)]) - } - - fn make_stmts(self: Box) -> Option> { - Some(smallvec![ast::Stmt { - id: ast::DUMMY_NODE_ID, - span: self.item.span, - kind: ast::StmtKind::Item(Box::new(self.item)), - }]) - } -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -668,20 +650,18 @@ pub(super) fn expand_global_asm<'cx>( return ExpandResult::Retry(()); }; match mac { - Ok(inline_asm) => Box::new(MacGlobalAsm { - item: ast::Item { - attrs: ast::AttrVec::new(), - id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), - vis: ast::Visibility { - span: sp.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, - tokens: None, - }, - span: sp, + Ok(inline_asm) => MacEager::items(smallvec![Box::new(ast::Item { + attrs: ast::AttrVec::new(), + id: ast::DUMMY_NODE_ID, + kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), + vis: ast::Visibility { + span: sp.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, tokens: None, }, - }), + span: sp, + tokens: None, + })]), Err(guar) => DummyResult::any(sp, guar), } } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index d9d7dd9c36208..d66d14bb881a1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,7 +1,7 @@ -use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety}; +use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety, ast}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, sym}; -use thin_vec::thin_vec; +use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; use crate::deriving::generic::*; @@ -41,6 +41,45 @@ pub(crate) fn expand_deriving_partial_ord( } else { true }; + + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let has_derive_ord = cx.resolver.has_derive_ord(container_id); + let is_simple_candidate = |params: &ThinVec| -> bool { + has_derive_ord + && !params.iter().any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + }; + + let default_substructure = combine_substructure(Box::new(|cx, span, substr| { + cs_partial_cmp(cx, span, substr, discr_then_data) + })); + let simple_substructure = combine_substructure(Box::new(|cx, span, _| { + cs_partial_cmp_simple(cx, span, cx.expr_ident(span, Ident::new(sym::other, span))) + })); + let (is_simple, substructure) = match item { + Annotatable::Item(annitem) => match &annitem.kind { + // For unit structs/zero-variant enums, the default generated code is better. + ItemKind::Struct(.., ast::VariantData::Unit(..)) => (false, default_substructure), + // Also for single fieldless variant enum + ItemKind::Enum(.., enum_def) if enum_def.variants.is_empty() => { + (false, default_substructure) + } + ItemKind::Enum(.., enum_def) + if enum_def.variants.len() == 1 + && matches!(enum_def.variants[0].data, ast::VariantData::Unit(..)) => + { + (false, default_substructure) + } + ItemKind::Struct(_, ast::Generics { params, .. }, _) + | ItemKind::Enum(_, ast::Generics { params, .. }, _) + if is_simple_candidate(params) => + { + (true, simple_substructure) + } + _ => (false, default_substructure), + }, + _ => (false, default_substructure), + }; + let partial_cmp_def = MethodDef { name: sym::partial_cmp, generics: Bounds::empty(), @@ -49,9 +88,7 @@ pub(crate) fn expand_deriving_partial_ord( ret_ty, attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, - combine_substructure: combine_substructure(Box::new(|cx, span, substr| { - cs_partial_cmp(cx, span, substr, discr_then_data) - })), + combine_substructure: substructure, }; let trait_def = TraitDef { @@ -68,7 +105,18 @@ pub(crate) fn expand_deriving_partial_ord( safety: Safety::Default, document: true, }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, is_simple) +} + +// Special case for the type deriving both `PartialOrd` and `Ord`. Builds: +// ``` +// Some(::core::cmp::Ord::cmp(self, other)) +// ``` +fn cs_partial_cmp_simple(cx: &ExtCtxt<'_>, span: Span, other_expr: Box) -> BlockOrExpr { + let ord_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); + let cmp_expr = + cx.expr_call_global(span, ord_cmp_path, thin_vec![cx.expr_self(span), other_expr]); + BlockOrExpr::new_expr(cx.expr_some(span, cmp_expr)) } fn cs_partial_cmp( @@ -98,7 +146,8 @@ fn cs_partial_cmp( |cx, fold| match fold { CsFold::Single(field) => { let [other_expr] = &field.other_selflike_exprs[..] else { - cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); + cx.dcx() + .span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`"); }; let args = thin_vec![field.self_expr.clone(), other_expr.clone()]; cx.expr_call_global(field.span, partial_cmp_path.clone(), args) diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index e0810a35b040b..ed313859aeafa 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; use rustc_session::Session; #[cfg(feature = "master")] use rustc_session::config::DebugInfo; -use rustc_span::{DUMMY_SP, Span, respan}; +use rustc_span::{DUMMY_SP, Span, Symbol, respan}; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi}; #[cfg(feature = "master")] @@ -497,6 +497,10 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { None } } + + fn intrinsic_call_expects_place_always(&self, _name: Symbol) -> bool { + true + } } impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 6fb25174bf1e0..d9451bc5db11d 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -9,6 +9,7 @@ use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; +use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; #[cfg(feature = "master")] @@ -194,11 +195,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &mut self, instance: Instance<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], - result: PlaceRef<'tcx, RValue<'gcc>>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>>, span: Span, - ) -> Result<(), Instance<'tcx>> { + ) -> IntrinsicResult<'tcx, RValue<'gcc>> { let tcx = self.tcx; + let result = PlaceRef { val: result_place.unwrap(), layout: result_layout }; + let name = tcx.item_name(instance.def_id()); let name_str = name.as_str(); let fn_args = instance.args; @@ -353,7 +357,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc args[2].immediate(), result, ); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::breakpoint => { unimplemented!(); @@ -375,12 +379,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::unaligned_volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.unaligned_volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::prefetch_read_data | sym::prefetch_write_data @@ -448,12 +452,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc _ => bug!(), }, None => { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty: args[0].layout.ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } } } @@ -544,7 +548,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc extended_asm.set_volatile_flag(true); // We have copied the value to `result` already. - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::ptr_mask => { @@ -569,12 +573,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc span, ) { Ok(value) => value, - Err(()) => return Ok(()), + Err(err) => return IntrinsicResult::Err(err), } } // Fall back to default body - _ => return Err(Instance::new_raw(instance.def_id(), instance.args)), + _ => { + let fallback = Instance::new_raw(instance.def_id(), instance.args); + return IntrinsicResult::Fallback(fallback); + } }; if result.layout.ty.is_bool() { @@ -583,7 +590,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } else if !result.layout.ty.is_unit() { self.store_to_place(value, result.val); } - Ok(()) + IntrinsicResult::WroteIntoPlace } fn codegen_llvm_intrinsic_call( @@ -694,13 +701,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.context.new_rvalue_from_int(self.int_type, 0) } - fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { + fn va_start(&mut self, _va_list: RValue<'gcc>) { unimplemented!(); } - fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { + fn va_end(&mut self, _va_list: RValue<'gcc>) { // FIXME(antoyo): implement. - self.context.new_rvalue_from_int(self.int_type, 0) } fn retag_reg(&mut self, _ptr: Self::Value, _info: &RetagInfo) -> Self::Value { @@ -1352,7 +1358,10 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( dest: PlaceRef<'tcx, RValue<'gcc>>, ) { if !bx.sess().panic_strategy().unwinds() { - bx.call(bx.type_void(), None, None, try_func, &[data], None, None); + let param_type = bx.u8_type.make_pointer(); + let fn_type = + bx.context.new_function_pointer_type(None, bx.type_void(), &[param_type], false); + bx.call(fn_type, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. OperandValue::Immediate(bx.const_i32(0)).store(bx, dest); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index ff5155f9f7776..82ef99703b253 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -17,7 +17,7 @@ use rustc_hir as hir; use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use crate::builder::Builder; #[cfg(not(feature = "master"))] @@ -32,12 +32,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span, -) -> Result, ()> { +) -> Result, ErrorGuaranteed> { // macros for error handling: macro_rules! return_error { ($err:expr) => {{ - bx.tcx.dcx().emit_err($err); - return Err(()); + let err = bx.tcx.dcx().emit_err($err); + return Err(err); }}; } macro_rules! require { @@ -803,11 +803,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( bx: &mut Builder<'_, 'gcc, 'tcx>, span: Span, args: &[OperandRef<'tcx, RValue<'gcc>>], - ) -> Result, ()> { + ) -> Result, ErrorGuaranteed> { macro_rules! return_error { ($err:expr) => {{ - bx.tcx.dcx().emit_err($err); - return Err(()); + let err = bx.tcx.dcx().emit_err($err); + return Err(err); }}; } let ty::Float(ref f) = *in_elem.kind() else { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 8f1f80354771c..de5ff0dfe54ed 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -5,7 +5,7 @@ use rustc_hir::find_attr; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, TargetFeature, }; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; @@ -41,23 +41,25 @@ pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { } /// Get LLVM attribute for the provided inline heuristic. -pub(crate) fn inline_attr<'ll, 'tcx>( +#[inline] +pub(crate) fn inline_attr<'tcx, 'll>( cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>, - instance: ty::Instance<'tcx>, + instance: Instance<'tcx>, + codegen_fn_attrs: &CodegenFnAttrs, ) -> Option<&'ll Attribute> { + if !tcx.sess.opts.unstable_opts.inline_llvm { + // disable LLVM inlining + return Some(AttributeKind::NoInline.create_attr(cx.llcx)); + } + // `optnone` requires `noinline` - let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, (inline, _) => inline, }; - if !tcx.sess.opts.unstable_opts.inline_llvm { - // disable LLVM inlining - return Some(AttributeKind::NoInline.create_attr(cx.llcx)); - } match inline { InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)), InlineAttr::Always | InlineAttr::Force { .. } => { @@ -418,6 +420,10 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } + if let Some(instance) = instance { + to_add.extend(inline_attr(cx, tcx, instance, codegen_fn_attrs)); + } + if sess.must_emit_unwind_tables() { to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind)); } @@ -568,16 +574,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); - // Apply function attributes as per usual if there are no user defined - // target features otherwise this will get applied at the callsite. - if function_features.is_empty() { - if let Some(instance) = instance - && let Some(inline_attr) = inline_attr(cx, tcx, instance) - { - to_add.push(inline_attr); - } - } - to_add.extend(default_arm64e_ptrauth_attrs(cx, sess)); let function_features = function_features diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index b3655da885d96..e05c14691809c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -15,7 +15,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature, TargetFeatureKind}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, TyAndLayout, @@ -1446,20 +1446,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { if let Some(callee_instance) = callee_instance { // Attributes on the function definition being called let callee_attrs = self.cx.tcx.codegen_fn_attrs(callee_instance.def_id()); - if let Some(caller_attrs) = caller_attrs - // If there is an inline attribute and a target feature that matches - // we will add the attribute to the callsite otherwise we'll omit - // this and not add the attribute to prevent soundness issues. - && let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance) - && self.cx.tcx.is_target_feature_call_safe( - &callee_attrs.target_features, - &caller_attrs.target_features.iter().cloned().chain( - self.cx.tcx.sess.target_features.iter().map(|feat| TargetFeature { - name: *feat, - kind: TargetFeatureKind::Implied, - }) - ).collect::>(), - ) + + if let Some(inlining_rule) = + attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance, callee_attrs) { attributes::apply_to_callsite( call, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 7ce10bc696f49..04900142ed220 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -25,7 +25,7 @@ use rustc_session::Session; use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; -use rustc_span::{DUMMY_SP, Span, Spanned, Symbol}; +use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{ Arch, CfgAbi, Env, HasTargetSpec, Os, RelocModel, SmallDataThresholdSupport, Target, TlsModel, @@ -991,6 +991,17 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { None } } + + fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { + matches!( + name, + sym::autodiff + | sym::catch_unwind + | sym::volatile_load + | sym::unaligned_volatile_load + | sym::black_box + ) + } } impl<'ll> CodegenCx<'ll, '_> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index d819ee48ba7a1..7de8659fe3d41 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -10,6 +10,7 @@ use rustc_codegen_ssa::RetagInfo; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization}; +use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; @@ -24,7 +25,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::errors::feature_err; use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; use rustc_target::spec::{Arch, Os}; @@ -175,9 +176,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, &'ll Value>], - result: PlaceRef<'tcx, &'ll Value>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, span: Span, - ) -> Result<(), ty::Instance<'tcx>> { + ) -> IntrinsicResult<'tcx, &'ll Value> { let tcx = self.tcx; let llvm_version = crate::llvm_util::get_version(); @@ -222,8 +224,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) } sym::autodiff => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; codegen_autodiff(self, tcx, instance, args, result); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::offload => { if tcx.sess.opts.unstable_opts.offload.is_empty() { @@ -235,7 +241,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } codegen_offload(self, tcx, instance, args); - return Ok(()); + // offload *has* a return type, but somehow works without mentioning the place + return IntrinsicResult::WroteIntoPlace; } sym::is_val_statically_known => { if let OperandValue::Immediate(imm) = args[0].val { @@ -264,8 +271,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let ptr = select(self, true_val.llval, false_val.llval); let selected = OperandValue::Ref(PlaceValue::new_sized(ptr, true_val.align)); + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; selected.store(self, result); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } (OperandValue::Immediate(_), OperandValue::Immediate(_)) | (OperandValue::Pair(_, _), OperandValue::Pair(_, _)) => { @@ -273,11 +284,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let false_val = args[2].immediate_or_packed_pair(self); select(self, true_val, false_val) } - (OperandValue::ZeroSized, OperandValue::ZeroSized) => return Ok(()), + (OperandValue::ZeroSized, OperandValue::ZeroSized) => return IntrinsicResult::Operand(OperandValue::ZeroSized), _ => span_bug!(span, "Incompatible OperandValue for select_unpredictable"), } } sym::catch_unwind => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; catch_unwind_intrinsic( self, args[0].immediate(), @@ -285,7 +300,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { args[2].immediate(), result, ); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), sym::va_arg => { @@ -299,7 +314,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { feature_err(&*self.sess(), feature, span, msg).emit(); } - let BackendRepr::Scalar(scalar) = result.layout.backend_repr else { + let BackendRepr::Scalar(scalar) = result_layout.backend_repr else { bug!("the va_arg intrinsic does not support non-scalar types") }; @@ -316,7 +331,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { bug!("the va_arg intrinsic does not support `i128`/`u128`") } Primitive::Int(..) => { - let int_width = self.cx().size_of(result.layout.ty).bits(); + let int_width = self.cx().size_of(result_layout.ty).bits(); let target_c_int_width = self.cx().sess().target.options.c_int_width; if int_width < u64::from(target_c_int_width) { // Smaller integer types are automatically promototed and `va_arg` @@ -346,34 +361,39 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } - emit_va_arg(self, args[0], result.layout.ty) + emit_va_arg(self, args[0], result_layout.ty) } sym::volatile_load | sym::unaligned_volatile_load => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; + let ptr = args[0].immediate(); - let load = self.volatile_load(result.layout.llvm_type(self), ptr); + let load = self.volatile_load(result_layout.llvm_type(self), ptr); let align = if name == sym::unaligned_volatile_load { 1 } else { - result.layout.align.bytes() as u32 + result_layout.align.bytes() as u32 }; unsafe { llvm::LLVMSetAlignment(load, align); } - if !result.layout.is_zst() { + if !result_layout.is_zst() { self.store_to_place(load, result.val); } - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } sym::unaligned_volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.unaligned_volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } sym::prefetch_read_data | sym::prefetch_write_data @@ -397,7 +417,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.const_i32(locality), self.const_i32(cache_type), ], - ) + ); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } sym::carrying_mul_add => { let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx); @@ -435,12 +456,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::carryless_mul if llvm_version >= (22, 0, 0) => { let ty = args[0].layout.ty; if !ty.is_integral() { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } let (size, _) = ty.int_size_and_signed(self.tcx); let width = size.bits(); @@ -464,12 +485,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::unchecked_funnel_shr => { let ty = args[0].layout.ty; if !ty.is_integral() { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } let (size, signed) = ty.int_size_and_signed(self.tcx); let width = size.bits(); @@ -485,12 +506,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; let ret = self.call_intrinsic(llvm_name, &[llty], &[args[0].immediate(), y]); - self.intcast(ret, result.layout.llvm_type(self), false) + self.intcast(ret, result_layout.llvm_type(self), false) } sym::ctpop => { let ret = self.call_intrinsic("llvm.ctpop", &[llty], &[args[0].immediate()]); - self.intcast(ret, result.layout.llvm_type(self), false) + self.intcast(ret, result_layout.llvm_type(self), false) } sym::bswap => { if width == 8 { @@ -552,12 +573,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Scalar(_) | ScalarPair(_, _) => true, SimdVector { .. } => false, SimdScalableVector { .. } => { - tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { span, name: sym::raw_eq, ty: tp_ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } Memory { .. } => { // For rusty ABIs, small aggregates are actually passed @@ -595,6 +616,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::black_box => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; args[0].val.store(self, result); let result_val_span = [result.val.llval]; // We need to "use" the argument in some way LLVM can't introspect, and on @@ -629,7 +654,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`")); // We have copied the value to `result` already. - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::gpu_launch_sized_workgroup_mem => { @@ -653,7 +678,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.type_array(self.type_i8(), 0), AddressSpace::GPU_WORKGROUP, ); - let ty::RawPtr(inner_ty, _) = result.layout.ty.kind() else { unreachable!() }; + let ty::RawPtr(inner_ty, _) = result_layout.ty.kind() else { unreachable!() }; // The alignment of the global is used to specify the *minimum* alignment that // must be obeyed by the GPU runtime. // When multiple of these global variables are used by a kernel, the maximum alignment is taken. @@ -817,10 +842,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ); } - let llret_ty = if result.layout.ty.is_simd() - && let BackendRepr::Memory { .. } = result.layout.backend_repr + let llret_ty = if result_layout.ty.is_simd() + && let BackendRepr::Memory { .. } = result_layout.backend_repr { - let (size, elem_ty) = result.layout.ty.simd_size_and_type(self.tcx()); + let (size, elem_ty) = result_layout.ty.simd_size_and_type(self.tcx()); let elem_ll_ty = match elem_ty.kind() { ty::Float(f) => self.type_float_from_ty(*f), ty::Int(i) => self.type_int_from_ty(*i), @@ -830,7 +855,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; self.type_vector(elem_ll_ty, size) } else { - result.layout.llvm_type(self) + result_layout.llvm_type(self) }; match generic_simd_intrinsic( @@ -838,14 +863,14 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { name, fn_args, &loaded_args, - result.layout.ty, + result_layout.ty, llret_ty, span, ) { Ok(llval) => llval, // If there was an error, just skip this invocation... we'll abort compilation // anyway, but we can keep codegen'ing to find more errors. - Err(()) => return Ok(()), + Err(err) => return IntrinsicResult::Err(err), } } @@ -875,17 +900,23 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { _ => { debug!("unknown intrinsic '{}' -- falling back to default body", name); // Call the fallback body instead of generating the intrinsic code - return Err(ty::Instance::new_raw(instance.def_id(), instance.args)); + let fallback = ty::Instance::new_raw(instance.def_id(), instance.args); + return IntrinsicResult::Fallback(fallback); } }; - if result.layout.ty.is_bool() { - let val = self.from_immediate(llval); - self.store_to_place(val, result.val); - } else if !result.layout.ty.is_unit() { - self.store_to_place(llval, result.val); + if let BackendRepr::Memory { .. } = result_layout.backend_repr { + // We have an llvm immediate, but that's not what cg_ssa expects, + // so write it into the place (that always exists for memory) + if !result_layout.is_zst() { + self.store_to_place(llval, result_place.unwrap()); + } + IntrinsicResult::WroteIntoPlace + } else { + IntrinsicResult::Operand( + OperandRef::from_immediate_or_packed_pair(self, llval, result_layout).val, + ) } - Ok(()) } fn codegen_llvm_intrinsic_call( @@ -1042,12 +1073,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.extract_value(type_checked_load, 0) } - fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.va_start", &[self.val_ty(va_list)], &[va_list]) + fn va_start(&mut self, va_list: &'ll Value) { + self.call_intrinsic("llvm.va_start", &[self.val_ty(va_list)], &[va_list]); } - fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) + fn va_end(&mut self, va_list: &'ll Value) { + self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]); } fn retag_reg(&mut self, ptr: Self::Value, info: &RetagInfo) -> Self::Value { @@ -2001,11 +2032,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ret_ty: Ty<'tcx>, llret_ty: &'ll Type, span: Span, -) -> Result<&'ll Value, ()> { +) -> Result<&'ll Value, ErrorGuaranteed> { macro_rules! return_error { ($diag: expr) => {{ - bx.sess().dcx().emit_err($diag); - return Err(()); + let err = bx.sess().dcx().emit_err($diag); + return Err(err); }}; } @@ -2452,11 +2483,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, span: Span, args: &[OperandRef<'tcx, &'ll Value>], - ) -> Result<&'ll Value, ()> { + ) -> Result<&'ll Value, ErrorGuaranteed> { macro_rules! return_error { ($diag: expr) => {{ - bx.sess().dcx().emit_err($diag); - return Err(()); + let err = bx.sess().dcx().emit_err($diag); + return Err(err); }}; } @@ -3042,7 +3073,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.$red(input); - Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) }) + Ok(r) } _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1e1be9d3a544f..b4626724fd803 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -419,16 +419,16 @@ fn check_result( // llvm/llvm-project#70563). if !codegen_fn_attrs.target_features.is_empty() && matches!(codegen_fn_attrs.inline, InlineAttr::Always) - && !tcx.features().target_feature_inline_always() && let Some(span) = interesting_spans.inline { - feature_err( - tcx.sess, - sym::target_feature_inline_always, - span, - "cannot use `#[inline(always)]` with `#[target_feature]`", - ) - .emit(); + let mut diag = tcx + .dcx() + .struct_span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); + diag.note( + "See this issue for full discussion: \ + https://github.com/rust-lang/rust/issues/145574", + ); + diag.emit(); } // warn that inline has no effect when no_sanitize is present diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index a1d3add23718a..f04a8a57f1014 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1220,14 +1220,17 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[derive(Diagnostic)] #[diag("target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}")] -#[note( - "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" -)] -#[note("for more information, see issue #116344 ")] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, pub reason: &'a str, + #[note( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" + )] + #[note( + "for more information, see issue #116344 " + )] + pub future_compat_note: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index de755d5617801..fb5734ba087c6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -7,8 +7,8 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal}; -use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::{bug, span_bug}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::{bug, span_bug, ty}; use tracing::debug; use super::FunctionCx; @@ -55,7 +55,7 @@ pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( non_ssa_locals } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] enum LocalKind { ZST, /// A local that requires an alloca. @@ -195,12 +195,20 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer match context { PlaceContext::MutatingUse(MutatingUseContext::Call) => { let call = location.block; - let TerminatorKind::Call { target, .. } = - self.fx.mir.basic_blocks[call].terminator().kind + let TerminatorKind::Call { target, func, .. } = + &self.fx.mir.basic_blocks[call].terminator().kind else { bug!() }; - self.define(local, DefLocation::CallReturn { call, target }); + let tcx = self.fx.cx.tcx(); + let func_ty = func.ty(&self.fx.mir.local_decls, tcx); + if let ty::FnDef(def_id, _args) = *func_ty.kind() + && let Some(intrinsic) = tcx.intrinsic(def_id) + && self.fx.cx.intrinsic_call_expects_place_always(intrinsic.name) + { + self.locals[local] = LocalKind::Memory; + } + self.define(local, DefLocation::CallReturn { call, target: *target }); } PlaceContext::NonUse(_) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f4e08e08ef8db..b6b95c5f12aae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -17,12 +17,13 @@ use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode} use tracing::{debug, info}; use super::operand::OperandRef; -use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized}; +use super::operand::OperandValue::{self, Immediate, Pair, Ref, ZeroSized}; use super::place::{PlaceRef, PlaceValue}; use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; +use crate::mir::IntrinsicResult; use crate::traits::*; use crate::{MemFlags, meth}; @@ -944,32 +945,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); - let (result, store_in_local) = if result_layout.is_zst() { - ( - PlaceRef::new_sized(bx.const_undef(bx.type_ptr()), result_layout), - None, - ) - } else if let Some(local) = destination.as_local() { - match self.locals[local] { - LocalRef::Place(dest) => (dest, None), - LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), - LocalRef::PendingOperand => { - // Currently, intrinsics always need a location to store - // the result, so we create a temporary `alloca` for the - // result. - let tmp = PlaceRef::alloca(bx, result_layout); - tmp.storage_live(bx); - (tmp, Some(local)) + let (result_place, store_in_local) = + if let Some(local) = destination.as_local() { + match self.locals[local] { + LocalRef::Place(dest) => (Some(dest.val), None), + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + LocalRef::PendingOperand => (None, Some(local)), + LocalRef::Operand(_) => { + if result_layout.is_zst() { + let place = PlaceRef::new_sized( + bx.const_undef(bx.type_ptr()), + result_layout, + ); + (Some(place.val), None) + } else { + bug!("place local already assigned to"); + } + } } - LocalRef::Operand(_) => { - bug!("place local already assigned to"); - } - } - } else { - (self.codegen_place(bx, destination.as_ref()), None) - }; + } else { + (Some(self.codegen_place(bx, destination.as_ref()).val), None) + }; - if result.val.align < result.layout.align.abi { + if let Some(place) = result_place + && place.align < result_layout.align.abi + { // Currently, MIR code generation does not create calls // that store directly to fields of packed structs (in // fact, the calls it creates write only to temps). @@ -982,16 +982,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let args: Vec<_> = args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); - match self.codegen_intrinsic_call(bx, instance, &args, result, source_info) - { - Ok(()) => { - if let Some(local) = store_in_local { - let op = bx.load_operand(result); - result.storage_dead(bx); + let intrinsic_result = self.codegen_intrinsic_call( + bx, + instance, + &args, + result_layout, + result_place, + source_info, + ); + + if let IntrinsicResult::Operand(op_val) = intrinsic_result { + match (result_place, store_in_local) { + (None, Some(local)) => { + let op = OperandRef { + val: op_val, + layout: result_layout, + move_annotation: None, + }; self.overwrite_local(local, LocalRef::Operand(op)); self.debug_introduce_local(bx, local); } + (Some(place_val), None) => { + let dest = PlaceRef { val: place_val, layout: result_layout }; + op_val.store(bx, dest); + } + _ => bug!(), + } + } + match intrinsic_result { + IntrinsicResult::Operand(_) | IntrinsicResult::WroteIntoPlace => { return if let Some(target) = target { helper.funclet_br(self, bx, target, mergeable_succ) } else { @@ -999,7 +1019,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MergingSucc::False }; } - Err(instance) => { + IntrinsicResult::Err(_) => { + // Even though we're definitely going to error, we need it initialize + // the local or `maybe_codegen_consume_direct` might ICE later + // when it goes to use the result from this intrinsic. + if let Some(local) = store_in_local { + let op = OperandRef { + val: OperandValue::poison(bx, result_layout), + layout: result_layout, + move_annotation: None, + }; + self.overwrite_local(local, LocalRef::Operand(op)); + } + // Also we need to terminate the block to avoid an LLVM assertion, + // even though we're not going to actually use the IR. + bx.abort(); + return MergingSucc::False; + } + IntrinsicResult::Fallback(instance) => { if intrinsic.must_be_overridden { span_bug!( fn_span, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index a11e584207e2f..ac6c6a0a52efa 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,16 +1,17 @@ -use rustc_abi::{Align, WrappingRange}; +use rustc_abi::{Align, FieldIdx, WrappingRange}; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; -use rustc_span::sym; +use rustc_span::{ErrorGuaranteed, sym}; use rustc_target::spec::Arch; -use super::FunctionCx; -use super::operand::OperandRef; -use super::place::PlaceRef; +use super::operand::{OperandRef, OperandValue}; +use super::place::PlaceValue; +use super::{FunctionCx, IntrinsicResult}; use crate::common::{AtomicRmwBinOp, SynchronizationScope}; use crate::errors::InvalidMonomorphization; +use crate::mir::operand::OperandRefBuilder; use crate::traits::*; use crate::{MemFlags, meth, size_of_val}; @@ -52,15 +53,16 @@ fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - /// In the `Err` case, returns the instance that should be called instead. + /// In the `Fallback` case, returns the instance that should be called instead. pub fn codegen_intrinsic_call( &mut self, bx: &mut Bx, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Bx::Value>], - result: PlaceRef<'tcx, Bx::Value>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, source_info: SourceInfo, - ) -> Result<(), ty::Instance<'tcx>> { + ) -> IntrinsicResult<'tcx, Bx::Value> { let span = source_info.span; let name = bx.tcx().item_name(instance.def_id()); @@ -86,19 +88,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let x_place = args[0].val.deref(align); let y_place = args[1].val.deref(align); bx.typed_place_swap(x_place, y_place, pointee_layout); - return Ok(()); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } } - let invalid_monomorphization_int_type = |ty| { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); + let invalid_monomorphization_int_type = |ty| -> ErrorGuaranteed { + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }) }; - let invalid_monomorphization_int_or_ptr_type = |ty| { + let invalid_monomorphization_int_or_ptr_type = |ty| -> ErrorGuaranteed { bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerOrPtrType { span, name, ty, - }); + }) }; let parse_atomic_ordering = |ord: ty::Value<'tcx>| { @@ -137,32 +139,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let llval = match name { + let op_val: OperandValue<_> = match name { sym::abort => { bx.abort(); - return Ok(()); + OperandValue::ZeroSized } sym::caller_location => { let location = self.get_caller_location(bx, source_info); - location.val.store(bx, result); - return Ok(()); + location.val } // va_end uses the fallback body (a no-op). - sym::va_start => bx.va_start(args[0].immediate()), + sym::va_start => { + bx.va_start(args[0].immediate()); + OperandValue::ZeroSized + } sym::size_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); - llsize + OperandValue::Immediate(llsize) } sym::align_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); - llalign + OperandValue::Immediate(llalign) } sym::vtable_size | sym::vtable_align => { let vtable = args[0].immediate(); @@ -190,14 +194,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => {} } - value + OperandValue::Immediate(value) } sym::arith_offset => { let ty = fn_args.type_at(0); let layout = bx.layout_of(ty); let ptr = args[0].immediate(); let offset = args[1].immediate(); - bx.gep(bx.backend_type(layout), ptr, &[offset]) + OperandValue::Immediate(bx.gep(bx.backend_type(layout), ptr, &[offset])) } sym::copy => { copy_intrinsic( @@ -209,7 +213,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[0].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::write_bytes => { memset_intrinsic( @@ -220,7 +224,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_copy_nonoverlapping_memory => { @@ -233,7 +237,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_copy_memory => { copy_intrinsic( @@ -245,7 +249,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_set_memory => { memset_intrinsic( @@ -256,60 +260,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_store => { let dst = args[0].deref(bx.cx()); args[1].val.volatile_store(bx, dst); - return Ok(()); + OperandValue::ZeroSized } sym::unaligned_volatile_store => { let dst = args[0].deref(bx.cx()); args[1].val.unaligned_volatile_store(bx, dst); - return Ok(()); + OperandValue::ZeroSized } sym::disjoint_bitor => { let a = args[0].immediate(); let b = args[1].immediate(); - bx.or_disjoint(a, b) + OperandValue::Immediate(bx.or_disjoint(a, b)) } sym::exact_div => { let ty = args[0].layout.ty; match int_type_width_signed(ty, bx.tcx()) { - Some((_width, signed)) => { - if signed { - bx.exactsdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.exactudiv(args[0].immediate(), args[1].immediate()) - } - } + Some((_width, signed)) => OperandValue::Immediate(if signed { + bx.exactsdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.exactudiv(args[0].immediate(), args[1].immediate()) + }), None => { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return Ok(()); + let err = bx + .tcx() + .dcx() + .emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); + return IntrinsicResult::Err(err); } } } sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { match float_type_width(args[0].layout.ty) { - Some(_width) => match name { + Some(_width) => OperandValue::Immediate(match name { sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()), sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()), sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()), sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()), _ => bug!(), - }, + }), None => { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { - span, - name, - ty: args[0].layout.ty, - }); - return Ok(()); + let err = + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { + span, + name, + ty: args[0].layout.ty, + }); + return IntrinsicResult::Err(err); } } } @@ -318,7 +320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::fmul_algebraic | sym::fdiv_algebraic | sym::frem_algebraic => match float_type_width(args[0].layout.ty) { - Some(_width) => match name { + Some(_width) => OperandValue::Immediate(match name { sym::fadd_algebraic => { bx.fadd_algebraic(args[0].immediate(), args[1].immediate()) } @@ -335,75 +337,77 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.frem_algebraic(args[0].immediate(), args[1].immediate()) } _ => bug!(), - }, + }), None => { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { + let err = bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { span, name, ty: args[0].layout.ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } }, sym::float_to_int_unchecked => { if float_type_width(args[0].layout.ty).is_none() { - bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { - span, - ty: args[0].layout.ty, - }); - return Ok(()); + let err = + bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { + span, + ty: args[0].layout.ty, + }); + return IntrinsicResult::Err(err); } - let Some((_width, signed)) = int_type_width_signed(result.layout.ty, bx.tcx()) + let Some((_width, signed)) = int_type_width_signed(result_layout.ty, bx.tcx()) else { - bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { - span, - ty: result.layout.ty, - }); - return Ok(()); + let err = + bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { + span, + ty: result_layout.ty, + }); + return IntrinsicResult::Err(err); }; - if signed { - bx.fptosi(args[0].immediate(), bx.backend_type(result.layout)) + OperandValue::Immediate(if signed { + bx.fptosi(args[0].immediate(), bx.backend_type(result_layout)) } else { - bx.fptoui(args[0].immediate(), bx.backend_type(result.layout)) - } + bx.fptoui(args[0].immediate(), bx.backend_type(result_layout)) + }) } sym::atomic_load => { let ty = fn_args.type_at(0); if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } let ordering = fn_args.const_at(1).to_value(); let layout = bx.layout_of(ty); let source = args[0].immediate(); - bx.atomic_load( + OperandValue::Immediate(bx.atomic_load( bx.backend_type(layout), source, parse_atomic_ordering(ordering), layout.size, - ) + )) } sym::atomic_store => { let ty = fn_args.type_at(0); if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } let ordering = fn_args.const_at(1).to_value(); let size = bx.layout_of(ty).size; let val = args[1].immediate(); let ptr = args[0].immediate(); bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size); - return Ok(()); + OperandValue::ZeroSized } // These are all AtomicRMW ops sym::atomic_cxchg | sym::atomic_cxchgweak => { let ty = fn_args.type_at(0); if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } let succ_ordering = fn_args.const_at(1).to_value(); let fail_ordering = fn_args.const_at(2).to_value(); @@ -422,12 +426,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = bx.from_immediate(val); let success = bx.from_immediate(success); - let dest = result.project_field(bx, 0); - bx.store_to_place(val, dest.val); - let dest = result.project_field(bx, 1); - bx.store_to_place(success, dest.val); - - return Ok(()); + let mut builder = OperandRefBuilder::new(result_layout); + builder.insert_imm(FieldIdx::from_u32(0), val); + builder.insert_imm(FieldIdx::from_u32(1), success); + builder.build(bx.cx()).val } sym::atomic_max | sym::atomic_min => { let atom_op = if name == sym::atomic_max { @@ -441,16 +443,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ordering = fn_args.const_at(1).to_value(); let ptr = args[0].immediate(); let val = args[1].immediate(); - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atom_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ false, - ) + )) } else { - invalid_monomorphization_int_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_type(ty); + return IntrinsicResult::Err(err); } } sym::atomic_umax | sym::atomic_umin => { @@ -465,16 +467,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ordering = fn_args.const_at(1).to_value(); let ptr = args[0].immediate(); let val = args[1].immediate(); - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atom_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ false, - ) + )) } else { - invalid_monomorphization_int_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_type(ty); + return IntrinsicResult::Err(err); } } sym::atomic_xchg => { @@ -484,16 +486,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ptr = args[0].immediate(); let val = args[1].immediate(); let atomic_op = AtomicRmwBinOp::AtomicXchg; - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atomic_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ ty.is_raw_ptr(), - ) + )) } else { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } } sym::atomic_xadd @@ -525,22 +527,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { { let ptr = args[0].immediate(); // of type "pointer to `ty_mem`" let val = args[1].immediate(); // of type `ty_op` - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atom_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ ty_mem.is_raw_ptr(), - ) + )) } else { - invalid_monomorphization_int_or_ptr_type(ty_mem); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty_mem); + return IntrinsicResult::Err(err); } } sym::atomic_fence => { let ordering = fn_args.const_at(0).to_value(); bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread); - return Ok(()); + OperandValue::ZeroSized } sym::atomic_singlethreadfence => { @@ -549,13 +551,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { parse_atomic_ordering(ordering), SynchronizationScope::SingleThread, ); - return Ok(()); + OperandValue::ZeroSized } sym::nontemporal_store => { let dst = args[0].deref(bx.cx()); args[1].val.nontemporal_store(bx, dst); - return Ok(()); + OperandValue::ZeroSized } sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { @@ -567,7 +569,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let a = bx.ptrtoint(a, bx.type_isize()); let b = bx.ptrtoint(b, bx.type_isize()); let pointee_size = bx.const_usize(pointee_size.bytes()); - if name == sym::ptr_offset_from { + OperandValue::Immediate(if name == sym::ptr_offset_from { // This is the same sequence that Clang emits for pointer subtraction. // It can be neither `nsw` nor `nuw` because the input is treated as // unsigned but then the output is treated as signed, so neither works. @@ -579,27 +581,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // so can use `sub nuw` and `udiv exact` instead of dealing in signed. let d = bx.unchecked_usub(a, b); bx.exactudiv(d, pointee_size) - } + }) } sym::cold_path => { // This is a no-op. The intrinsic is just a hint to the optimizer. - return Ok(()); + OperandValue::ZeroSized } _ => { // Need to use backend-specific things in the implementation. - return bx.codegen_intrinsic_call(instance, args, result, span); + let result = + bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span); + if let IntrinsicResult::Operand(op) = result { + op + } else { + return result; + } } }; - if result.layout.ty.is_bool() { - let val = bx.from_immediate(llval); - bx.store_to_place(val, result.val); - } else if !result.layout.ty.is_unit() { - bx.store_to_place(llval, result.val); - } - Ok(()) + debug_assert!( + op_val.is_expected_variant_for_type(bx.cx(), result_layout), + "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}", + ); + + IntrinsicResult::Operand(op_val) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 84013a00d79df..4bcf037ecce07 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -7,6 +7,7 @@ use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::{bug, mir, span_bug}; +use rustc_span::ErrorGuaranteed; use rustc_target::callconv::{FnAbi, PassMode}; use tracing::{debug, instrument}; @@ -157,6 +158,29 @@ enum LocalRef<'tcx, V> { PendingOperand, } +pub enum IntrinsicResult<'tcx, V> { + /// This intrinsic created an operand without using the `result_place` argument. + /// + /// `codegen_call_terminator` will handle writing the result into the place, + /// if doing so is needed. + /// + /// The vast majority of intrinsics can do this, see MCP#970 + Operand(OperandValue), + + /// The intrinsic wrote its result into the `result_place` argument. + /// + /// Most things don't need to do this, but there are some: `volatile_load` + /// of a non-scalar type, for example, has to. + WroteIntoPlace, + + /// Another instance should be called instead. This is used to invoke intrinsic + /// default bodies in case an intrinsic is not implemented by the backend. + Fallback(ty::Instance<'tcx>), + + /// Arguably this shouldn't exist, per MCP#620, but a bunch do it. + Err(ErrorGuaranteed), +} + impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> { fn new_operand(layout: TyAndLayout<'tcx>) -> LocalRef<'tcx, V> { if layout.is_zst() { diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index e1d1ef858c017..83fce5a5c8deb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -103,6 +103,7 @@ impl OperandValue { PlaceValue { llval, llextra, align } } + #[must_use] pub(crate) fn is_expected_variant_for_type<'tcx, Cx: LayoutTypeCodegenMethods<'tcx>>( &self, cx: &Cx, diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index a039b3111b52a..45fd5ce05b0a1 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -325,12 +325,19 @@ pub fn cfg_target_feature<'a, const N: usize>( sess.dcx().emit_warn(unknown_feature); } Some((_, stability, _)) => { - if let Err(reason) = stability.toggle_allowed() { - sess.dcx().emit_warn(errors::ForbiddenCTargetFeature { + if let Stability::Forbidden { reason, hard_error } = stability { + let diag = errors::ForbiddenCTargetFeature { feature: base_feature, enabled: if enable { "enabled" } else { "disabled" }, reason, - }); + future_compat_note: !hard_error, + }; + + if *hard_error { + sess.dcx().emit_err(diag); + } else { + sess.dcx().emit_warn(diag); + } } else if stability.requires_nightly(/* in_cfg */ false).is_some() { // An unstable feature. Warn about using it. It makes little sense // to hard-error here since we just warn about fully unknown diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index fffd9b75f2bf7..dcd4e722a27a8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -3,8 +3,9 @@ use rustc_span::Span; use super::BackendTypes; use crate::RetagInfo; +use crate::mir::IntrinsicResult; use crate::mir::operand::OperandRef; -use crate::mir::place::PlaceRef; +use crate::mir::place::PlaceValue; pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Higher-level interface to emitting calls to intrinsics @@ -12,9 +13,12 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`, /// and in `library/core/src/intrinsics.rs`; if you need access to any LLVM intrinsics, /// add them to `compiler/rustc_codegen_llvm/src/context.rs`. - /// Returns `Err` if another instance should be called instead. This is used to invoke + /// Returns `Fallback` if another instance should be called instead. This is used to invoke /// intrinsic default bodies in case an intrinsic is not implemented by the backend. /// + /// The `result_place` will be provided for things that weren't `LocalKind::SSA`. + /// If you need it for more things, see `intrinsic_call_expects_place_always`. + /// /// NOTE: allowed to call [`BuilderMethods::call`] /// /// [`BuilderMethods::call`]: super::builder::BuilderMethods::call @@ -22,9 +26,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], - result_dest: PlaceRef<'tcx, Self::Value>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, span: Span, - ) -> Result<(), ty::Instance<'tcx>>; + ) -> IntrinsicResult<'tcx, Self::Value>; fn codegen_llvm_intrinsic_call( &mut self, @@ -46,10 +51,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { ) -> Self::Value; /// Trait method used to inject `va_start` on the "spoofed" `VaList` in /// Rust defined C-variadic functions. - fn va_start(&mut self, val: Self::Value) -> Self::Value; + fn va_start(&mut self, val: Self::Value); /// Trait method used to inject `va_end` on the "spoofed" `VaList` before /// Rust defined C-variadic functions return. - fn va_end(&mut self, val: Self::Value) -> Self::Value; + fn va_end(&mut self, val: Self::Value); /// Trait method used to retag a pointer stored within a place. fn retag_mem(&mut self, place: Self::Value, info: &RetagInfo); /// Trait method used to retag a pointer that has been loaded into a register. diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 0a06cdfe72bee..409751810c3b1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::Session; +use rustc_span::Symbol; use super::BackendTypes; @@ -29,4 +30,10 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { /// Declares the extern "C" main function for the entry point. Returns None if the symbol /// already exists. fn declare_c_main(&self, fn_type: Self::FunctionSignature) -> Option; + + /// Whether `codegen_intrinsic_call` expects to always have a `place_value` + /// when emitting code for the intrinsic `name`. + /// + /// This is discouraged, but here for now to simplify migration to using OperandValues + fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool; } diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 0cd0b51b6ad49..54699831e3717 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1,4 +1,5 @@ // This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in // `rustc_driver_impl` to be compiled in parallel with other crates. +#[doc(no_inline)] pub use rustc_driver_impl::*; diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 426ead704ab83..8138fb6a2ff40 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -239,7 +239,6 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { let annotation: Box = match s { Normal => Box::new(AstNoAnn), Expanded => Box::new(AstNoAnn), - Identified => Box::new(AstIdentifiedAnn), ExpandedIdentified => Box::new(AstIdentifiedAnn), ExpandedHygiene => Box::new(AstHygieneAnn { sess }), }; diff --git a/compiler/rustc_error_codes/src/error_codes/E0371.md b/compiler/rustc_error_codes/src/error_codes/E0371.md index a44721346e20d..aacaee0c1b6e0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0371.md +++ b/compiler/rustc_error_codes/src/error_codes/E0371.md @@ -7,10 +7,18 @@ trait Foo { fn foo(&self) { } } trait Bar: Foo { } trait Baz: Bar { } -impl Bar for Baz { } // error, `Baz` implements `Bar` by definition -impl Foo for Baz { } // error, `Baz` implements `Bar` which implements `Foo` -impl Baz for Baz { } // error, `Baz` (trivially) implements `Baz` -impl Baz for Bar { } // Note: This is OK +impl Bar for dyn Baz { } // error, `Baz` implements `Bar` by definition +impl Foo for dyn Baz { } // error, `Baz` implements `Bar` which implements `Foo` +impl Baz for dyn Baz { } // error, `Baz` (trivially) implements `Baz` +``` + +This is okay, because `Bar` does not implement `Baz` by definition: + +``` +# trait Foo { fn foo(&self) { } } +# trait Bar: Foo { } +# trait Baz: Bar { } +impl Baz for dyn Bar { } // Note: This is OK ``` When `Trait2` is a subtrait of `Trait1` (for example, when `Trait2` has a diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7f1b58dd1de08..9f474fbeb7650 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1117,6 +1117,8 @@ pub trait ResolverExpand { // Resolver interfaces for specific built-in macros. /// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Copy` inside it? fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool; + /// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Ord` inside it? + fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool; /// Resolve paths inside the `#[derive(...)]` attribute with the given `ExpnId`. fn resolve_derives( &mut self, diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 2db18429a5216..4044a414c5fee 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -186,6 +186,11 @@ pub(crate) fn placeholder( ty: ty(), vis, is_placeholder: true, + mut_restriction: ast::MutRestriction { + kind: ast::RestrictionKind::Unrestricted, + span: DUMMY_SP, + tokens: None, + }, safety: Safety::Default, default: None, }]), diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index a4746ac455c1d..3cd88cc4bc116 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e10b9a77be851..c61b8d66af426 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -3,6 +3,7 @@ use std::sync::LazyLock; use AttributeGate::*; +use rustc_ast::ast::Safety; use rustc_data_structures::fx::FxHashMap; use rustc_hir::AttrStyle; use rustc_span::{Symbol, sym}; @@ -113,6 +114,7 @@ impl AttributeTemplate { pub fn suggestions( &self, style: AttrSuggestionStyle, + safety: Safety, name: impl std::fmt::Display, ) -> Vec { let (start, macro_call, end) = match style { @@ -124,20 +126,32 @@ impl AttributeTemplate { let mut suggestions = vec![]; + let (safety_start, safety_end) = match safety { + Safety::Unsafe(_) => ("unsafe(", ")"), + _ => ("", ""), + }; + if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{start}{name}{end}")); + suggestions.push(format!("{start}{safety_start}{name}{safety_end}{end}")); } if let Some(descr) = self.list { for descr in descr { - suggestions.push(format!("{start}{name}{macro_call}({descr}){end}")); + suggestions.push(format!( + "{start}{safety_start}{name}{macro_call}({descr}){safety_end}{end}" + )); } } - suggestions.extend(self.one_of.iter().map(|&word| format!("{start}{name}({word}){end}"))); + suggestions.extend( + self.one_of + .iter() + .map(|&word| format!("{start}{safety_start}{name}({word}){safety_end}{end}")), + ); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { - suggestions.push(format!("{start}{name} = \"{descr}\"{end}")); + suggestions + .push(format!("{start}{safety_start}{name} = \"{descr}\"{safety_end}{end}")); } } suggestions.sort(); diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 7508fb7c250c7..53d5dda6d7f3f 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -282,6 +282,9 @@ declare_features! ( /// Allows string patterns to dereference values to match them. (removed, string_deref_patterns, "1.94.0", Some(87121), Some("superseded by `deref_patterns`"), 150530), (removed, struct_inherit, "1.0.0", None, None), + /// Allows the use of target_feature when a function is marked inline(always). + (removed, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574), + Some("removed because of unfixable soundness issues")), (removed, test_removed_feature, "1.0.0", None, None), /// Allows using items which are missing stability attributes (removed, unmarked_api, "1.0.0", None, None), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index fce8fe923a133..da45596d0b74e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -648,6 +648,8 @@ declare_features! ( (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "1.79.0", Some(123076)), + /// Allows `mut(crate) field: Type` restrictions. + (incomplete, mut_restriction, "CURRENT_RUSTC_VERSION", Some(105077)), /// Allows using `#[naked]` on `extern "Rust"` functions. (unstable, naked_functions_rustic_abi, "1.88.0", Some(138997)), /// Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions. @@ -727,8 +729,6 @@ declare_features! ( (unstable, super_let, "1.88.0", Some(139076)), /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), - /// Allows the use of target_feature when a function is marked inline(always). - (unstable, target_feature_inline_always, "1.91.0", Some(145574)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index 7cbb0ea45b969..7d4ae803cee59 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -147,11 +147,12 @@ impl FormatString { }; ret.push_str(&slf); } + Piece::Arg(FormatArg::This) => ret.push_str(&args.this), // It's only `rustc_onunimplemented` from here - Piece::Arg(FormatArg::This) => ret.push_str(&args.this), - Piece::Arg(FormatArg::Trait) => { - let _ = fmt::write(&mut ret, format_args!("{}", &args.this_sugared)); + Piece::Arg(FormatArg::ThisPath) => ret.push_str(&args.this_path), + Piece::Arg(FormatArg::ThisResolved) => { + let _ = fmt::write(&mut ret, format_args!("{}", &args.this_resolved)); } Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), } @@ -197,7 +198,7 @@ impl FormatString { /// ```rust,ignore (just an example) /// FormatArgs { /// this: "FromResidual", -/// this_sugared: "FromResidual>", +/// this_resolved: "FromResidual>", /// item_context: "an async function", /// generic_args: [("Self", "u32"), ("R", "Option")], /// } @@ -206,7 +207,8 @@ impl FormatString { pub struct FormatArgs { /// The name of the item the attribute is on. pub this: String, - pub this_sugared: String = String::new(), + pub this_resolved: String = String::new(), + pub this_path: String = String::new(), pub item_context: &'static str = "", pub generic_args: Vec<(Symbol, String)> = Vec::new(), } @@ -226,10 +228,12 @@ pub enum FormatArg { }, // `{Self}` SelfUpper, - /// `{This}` or `{TraitName}` + /// `{This}` and `{This:name}`. This, - /// The sugared form of the trait - Trait, + /// The sugared form: `{This:sugared}`. + ThisResolved, + /// The full path: `{This:path}`. + ThisPath, /// what we're in, like a function, method, closure etc. ItemContext, /// What the user typed, if it doesn't match anything we can use. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 0569c1b986d1a..59d1b4b5576ee 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3864,9 +3864,10 @@ pub enum OpaqueTyOrigin { }, } -// Ids of parent (or child) path segment that contains user-specified args #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] -pub struct DelegationGenerics { +pub struct DelegationInfo { + pub call_expr_id: HirId, + pub call_path_res: Option, pub parent_args_segment_id: Option, pub child_args_segment_id: Option, pub self_ty_id: Option, @@ -3876,8 +3877,8 @@ pub struct DelegationGenerics { #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] pub enum InferDelegationSig<'hir> { Input(usize), - // Place generics info here, as we always specify output type for delegations. - Output(&'hir DelegationGenerics), + // Place delegation info here, as we always specify output type for delegations. + Output(&'hir DelegationInfo), } #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] @@ -4164,7 +4165,7 @@ impl<'hir> FnDecl<'hir> { None } - pub fn opt_delegation_generics(&self) -> Option<&'hir DelegationGenerics> { + pub fn opt_delegation_info(&self) -> Option<&'hir DelegationInfo> { if let FnRetTy::Return(ty) = self.output && let TyKind::InferDelegation(InferDelegation::Sig(_, kind)) = ty.kind && let InferDelegationSig::Output(generics) = kind diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index f83433c2bd503..2ba7e026461f3 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -563,8 +563,8 @@ pub(crate) fn reborrow_info<'tcx>( ) .is_ok() { - // Field implements Reborrow. - return Ok(()); + // Field implements Reborrow, check remaining fields. + continue; } // Field does not implement Reborrow: it must be Copy. diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 2d6d5a5d81f9a..f67181a4655b9 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -7,7 +7,7 @@ use std::debug_assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{DelegationGenerics, HirId, PathSegment}; +use rustc_hir::{DelegationInfo, HirId, PathSegment}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; @@ -71,13 +71,17 @@ enum SelfPositionKind { None, } -fn get_delegation_generics(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> &DelegationGenerics { +pub fn opt_get_delegation_info( + tcx: TyCtxt<'_>, + delegation_id: LocalDefId, +) -> Option<&DelegationInfo> { tcx.hir_node(tcx.local_def_id_to_hir_id(delegation_id)) .fn_sig() - .expect("processing delegation") - .decl - .opt_delegation_generics() - .expect("processing delegation") + .and_then(|sig| sig.decl.opt_delegation_info()) +} + +fn get_delegation_info(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> &DelegationInfo { + opt_get_delegation_info(tcx, delegation_id).expect("processing delegation") } fn create_self_position_kind( @@ -92,7 +96,7 @@ fn create_self_position_kind( | (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero, (FnKind::Free, FnKind::AssocTrait) => { - let propagate_self_ty = get_delegation_generics(tcx, delegation_id).propagate_self_ty; + let propagate_self_ty = get_delegation_info(tcx, delegation_id).propagate_self_ty; SelfPositionKind::AfterLifetimes(propagate_self_ty) } @@ -278,7 +282,7 @@ fn get_parent_and_inheritance_kind<'tcx>( } fn get_delegation_self_ty_or_err(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> Ty<'_> { - get_delegation_generics(tcx, delegation_id) + get_delegation_info(tcx, delegation_id) .self_ty_id .map(|id| { let ctx = ItemCtxt::new(tcx, delegation_id); @@ -640,7 +644,7 @@ fn get_delegation_user_specified_args<'tcx>( tcx: TyCtxt<'tcx>, delegation_id: LocalDefId, ) -> (&'tcx [ty::GenericArg<'tcx>], &'tcx [ty::GenericArg<'tcx>]) { - let info = get_delegation_generics(tcx, delegation_id); + let info = get_delegation_info(tcx, delegation_id); let get_segment = |hir_id: HirId| -> Option<(&'tcx PathSegment<'tcx>, DefId)> { let segment = tcx.hir_node(hir_id).expect_path_segment(); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 9cadaef8f886b..1e9bc80749881 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -73,7 +73,7 @@ mod check_unused; mod coherence; mod collect; mod constrained_generic_params; -mod delegation; +pub mod delegation; pub mod errors; pub mod hir_ty_lowering; pub mod hir_wf_check; diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e15c9fe661641..d51a0bf2c3ef4 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -7,15 +7,16 @@ use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; +use rustc_hir_analysis::delegation::opt_get_delegation_info; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; +use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -27,6 +28,8 @@ use super::method::probe::ProbeScope; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::errors; use crate::method::TreatNotYetDefinedOpaques; +use crate::method::confirm::ConfirmContext; +use crate::method::probe::{IsSuggestion, Mode}; /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific @@ -591,16 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - fn_sig.output(), - expected, - arg_exprs, - fn_sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, - def_id, + self.check_argument_types_maybe_method_like( + &fn_sig, call_expr, arg_exprs, expected, def_id, ); if fn_sig.abi() == rustc_abi::ExternAbi::RustCall { @@ -620,6 +615,110 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output() } + /// Performs arguments check with an additional routine of adjusting the first argument, + /// so it corresponds to the first parameter of the function. We reuse adjustments + /// that are obtained from `probe_for_name`, where the first argument pretends to be + /// a receiver like in a method call. At this point this routine is used for delegations, + /// as from this moment we always generate a call (earlier method calls were generated), + /// so we can both propagate parent generics and get benefits from adjustments from method call. + fn check_argument_types_maybe_method_like( + &self, + fn_sig: &FnSig<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + def_id: Option, + ) { + let do_check = || { + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + fn_sig.output(), + expected, + arg_exprs, + fn_sig.c_variadic(), + TupleArgumentsFlag::DontTupleArguments, + def_id, + ); + }; + + let Some(scope) = self.get_scope_for_method_call_adjustments(call_expr, arg_exprs) else { + return do_check(); + }; + + let first_expr = &arg_exprs[0]; + let first_arg_type = self.check_expr(first_expr); + + // Reuse method probing that is used during method call, as all this code pretends that + // we generated method call. + let pick = self.probe_for_name( + Mode::MethodCall, + Ident::dummy(), + None, + IsSuggestion(false), + first_arg_type, + call_expr.hir_id, + scope, + ); + + let Ok(ref pick) = pick else { return do_check() }; + + // Fool typechecker by placing an adjusted type of the first arg to avoid errors. + // We already wrote type of `first_expr` during `self.check_expr(first_expr)` above. + let first_arg_type = self + .typeck_results + .borrow_mut() + .node_types_mut() + .insert(first_expr.hir_id, pick.self_ty) + .expect("must be set"); + + do_check(); + + let mut results = self.typeck_results.borrow_mut(); + + // Remove any added adjustments for `first_expr` during `do_check` and replace them with ours. + let mut adjustments = results.adjustments_mut(); + let adjustments = adjustments.entry(first_expr.hir_id).or_default(); + + let mut ctx = ConfirmContext::new(self, first_expr.span, first_expr, first_expr); + *adjustments = ctx.create_ty_adjustments_from_pick(first_arg_type, pick).1; + + // Restore original first provided arg type. + results.node_types_mut().insert(first_expr.hir_id, first_arg_type); + } + + /// Gets scope for method-call like adjustments for the first argument of the call. + /// Now only delegations are processed this way. + fn get_scope_for_method_call_adjustments( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + ) -> Option { + // Check that we are inside delegation and processing its call. First, we check that + // the parent of call expr. is delegation and then make sure that it is compiler-generated + // by comparing their hir ids (otherwise we will encounter errors in nested delegations, + // see tests\ui\delegation\impl-reuse-pass.rs:237). + let parent_def = self.tcx.hir_get_parent_item(call_expr.hir_id).def_id; + let Some(info) = opt_get_delegation_info(self.tcx, parent_def) else { return None }; + + if call_expr.hir_id != info.call_expr_id { + return None; + }; + + let Some(path_res_id) = info.call_path_res else { return None }; + + // Check that delegation has first provided arg and that the call path + // resolves to a trait method (inherent methods are not yet supported). + if arg_exprs.is_empty() + || !self.tcx.opt_associated_item(path_res_id).is_some_and(|i| i.is_method()) + { + return None; + } + + Some(ProbeScope::Single(path_res_id)) + } + /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)` /// and suggesting the fix if the method probe is successful. fn suggest_call_as_method( diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 4d934703f4670..8902505c11762 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -28,7 +28,6 @@ //! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! `U1` coerces to `U2`). -use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; @@ -860,19 +859,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast - (Ptr(m_expr), Int(t_c)) => { - self.lossy_provenance_ptr2int_lint(fcx, t_c); - self.check_ptr_addr_cast(fcx, m_expr) - } + (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), + (FnPtr, Int(_)) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast - (Int(_), Ptr(mt)) => { - self.fuzzy_provenance_int2ptr_lint(fcx); - self.check_addr_ptr_cast(fcx, mt) - } + (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // fn-ptr-cast (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), @@ -1133,55 +1127,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_prec = fcx.precedence(self.expr); - let needs_parens = expr_prec < ExprPrecedence::Unambiguous; - - let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); - let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - let expr_span = self.expr_span.shrink_to_lo(); - let sugg = match (needs_parens, needs_cast) { - (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_ty, - }, - (true, false) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty } - } - (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span }, - }; - - let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( - lint::builtin::LOSSY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - lint, - ); - } - - fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let sugg = errors::LossyProvenanceInt2PtrSuggestion { - lo: self.expr_span.shrink_to_lo(), - hi: self.expr_span.shrink_to_hi().to(self.cast_span), - }; - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( - lint::builtin::FUZZY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - lint, - ); - } - /// Attempt to suggest using `.is_empty` when trying to cast from a /// collection type to a boolean. fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diag<'_>) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 3f8ac61eed084..3457cc373413a 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -377,18 +377,6 @@ impl Subdiagnostic for TypeMismatchFruTypo { } } -#[derive(Diagnostic)] -#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] -#[help( - "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" -)] -pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { - pub expr_ty: Ty<'tcx>, - pub cast_ty: Ty<'tcx>, - #[subdiagnostic] - pub sugg: LossyProvenanceInt2PtrSuggestion, -} - #[derive(Diagnostic)] #[diag("cannot add {$traits_len -> [1] auto trait {$traits} @@ -404,76 +392,6 @@ pub(crate) struct PtrCastAddAutoToObject { pub traits: DiagSymbolList, } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", - applicability = "has-placeholders" -)] -pub(crate) struct LossyProvenanceInt2PtrSuggestion { - #[suggestion_part(code = "(...).with_addr(")] - pub lo: Span, - #[suggestion_part(code = ")")] - pub hi: Span, -} - -#[derive(Diagnostic)] -#[diag( - "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" -)] -#[help( - "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" -)] -pub(crate) struct LossyProvenancePtr2Int<'tcx> { - pub expr_ty: Ty<'tcx>, - pub cast_ty: Ty<'tcx>, - #[subdiagnostic] - pub sugg: LossyProvenancePtr2IntSuggestion<'tcx>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { - #[multipart_suggestion( - "use `.addr()` to obtain the address of a pointer", - applicability = "maybe-incorrect" - )] - NeedsParensCast { - #[suggestion_part(code = "(")] - expr_span: Span, - #[suggestion_part(code = ").addr() as {cast_ty}")] - cast_span: Span, - cast_ty: Ty<'tcx>, - }, - #[multipart_suggestion( - "use `.addr()` to obtain the address of a pointer", - applicability = "maybe-incorrect" - )] - NeedsParens { - #[suggestion_part(code = "(")] - expr_span: Span, - #[suggestion_part(code = ").addr()")] - cast_span: Span, - }, - #[suggestion( - "use `.addr()` to obtain the address of a pointer", - code = ".addr() as {cast_ty}", - applicability = "maybe-incorrect" - )] - NeedsCast { - #[primary_span] - cast_span: Span, - cast_ty: Ty<'tcx>, - }, - #[suggestion( - "use `.addr()` to obtain the address of a pointer", - code = ".addr()", - applicability = "maybe-incorrect" - )] - Other { - #[primary_span] - cast_span: Span, - }, -} - #[derive(Subdiagnostic)] pub(crate) enum HelpUseLatestEdition { #[help("set `edition = \"{$edition}\"` in `Cargo.toml`")] diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ed8081ea887f7..886bcc8c21823 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -171,7 +171,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .inspect(|vid| { let origin = self.float_var_origin(*vid); // Show the entire literal in the suggestion to make it clearer. - let literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok(); + let mut literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok(); + // A `.` at the end of the literal is no longer necessary if `f32` is explicitly specified + if let Some(ref mut literal) = literal + && literal.ends_with('.') + { + literal.pop(); + } self.tcx.emit_node_span_lint( FLOAT_LITERAL_F32_FALLBACK, origin.lint_id.unwrap_or(CRATE_HIR_ID), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 415630dab38b3..d62ba9cf804eb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, LangItem, Node, QPath, is_range_literal}; use rustc_hir_analysis::check::potentially_plural_count; +use rustc_hir_analysis::delegation::opt_get_delegation_info; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath}; use rustc_index::IndexVec; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace}; @@ -329,7 +330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let demand_compatible = |idx| { let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; - let provided_arg = &provided_args[idx]; + let provided_arg: &hir::Expr<'tcx> = &provided_args[idx]; debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); @@ -338,7 +339,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 1. Unify the provided argument with the expected type let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let checked_ty = self.check_expr_with_expectation(provided_arg, expectation); + // If we are processing first arg of delegation then we could have adjusted it + // in `execute_delegation_aware_arguments_check`. + let checked_ty = opt_get_delegation_info(self.tcx, self.body_id) + .and_then(|_| self.typeck_results.borrow().node_type_opt(provided_arg.hir_id)) + .unwrap_or_else(|| self.check_expr_with_expectation(provided_arg, expectation)); // 2. Coerce to the most detailed type that could be coerced // to, which is `expected_ty` if `rvalue_hint` returns an diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 323f0fb040d6b..6a9a2065f4d31 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2707,12 +2707,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(CtorKind::Const) => unreachable!("unit variants don't have fields"), }; - // Suggest constructor as deep into the block tree as possible. - // This fixes https://github.com/rust-lang/rust/issues/101065, - // and also just helps make the most minimal suggestions. + // Suggest constructor as deep into the block tree as possible, + // but don't cross macro contexts. This fixes #101065 while + // keeping suggestions out of macro definitions (#142359). let mut expr = expr; while let hir::ExprKind::Block(block, _) = &expr.kind && let Some(expr_) = &block.expr + && expr_.span.eq_ctxt(expr.span) { expr = expr_ } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 0c83c1948d6f2..4ef6de12f7623 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -34,7 +34,7 @@ use super::{MethodCallee, probe}; use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing}; use crate::{FnCtxt, callee}; -struct ConfirmContext<'a, 'tcx> { +pub(crate) struct ConfirmContext<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, self_expr: &'tcx hir::Expr<'tcx>, @@ -90,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { - fn new( + pub(crate) fn new( fcx: &'a FnCtxt<'a, 'tcx>, span: Span, self_expr: &'tcx hir::Expr<'tcx>, @@ -178,14 +178,32 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. + let (target, adjustments) = self.create_ty_adjustments_from_pick(unadjusted_self_ty, pick); + + // Write out the final adjustments. + if !self.skip_record_for_diagnostics { + self.apply_adjustments(self.self_expr, adjustments); + } + + target + } + + pub(crate) fn create_ty_adjustments_from_pick( + &mut self, + unadjusted_self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + ) -> (Ty<'tcx>, Vec>) { let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty); let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else { - return Ty::new_error_with_message( + let error_ty = Ty::new_error_with_message( self.tcx, DUMMY_SP, format!("failed autoderef {}", pick.autoderefs), ); + + return (error_ty, vec![]); }; + assert_eq!(n, pick.autoderefs); let mut adjustments = self.adjust_steps(&autoderef); @@ -260,12 +278,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.register_predicates(autoderef.into_obligations()); - // Write out the final adjustments. - if !self.skip_record_for_diagnostics { - self.apply_adjustments(self.self_expr, adjustments); - } - - target + (target, adjustments) } /// Returns a set of generic parameters for the method *receiver* where all type and region diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 49126ff0e964c..2ebff26ff9822 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -2,7 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir-typeck/method-lookup.html -mod confirm; +pub(crate) mod confirm; mod prelude_edition_lints; pub(crate) mod probe; mod suggest; diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 02e5feb6c5f21..2910ba7c46851 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -319,7 +319,7 @@ impl DenseBitSet { // quickly and accurately detect whether the update changed anything. // But that's only worth doing if there's an actual use-case. - bitwise(&mut self.words, &other.words, |a, b| a | !b); + update_words(&mut self.words, &other.words, |a, b| a | !b); // The bitwise update `a | !b` can result in the last word containing // out-of-domain bits, so we need to clear them. self.clear_excess_bits(); @@ -330,17 +330,17 @@ impl DenseBitSet { impl BitRelations> for DenseBitSet { fn union(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a | b) + update_words(&mut self.words, &other.words, |a, b| a | b) } fn subtract(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a & !b) + update_words(&mut self.words, &other.words, |a, b| a & !b) } fn intersect(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a & b) + update_words(&mut self.words, &other.words, |a, b| a & b) } } @@ -787,7 +787,7 @@ impl BitRelations> for ChunkedBitSet { // Do a more precise "will anything change?" test. Also a // performance win. let op = |a, b| a | b; - if !bitwise_changes( + if !would_modify_words( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -797,7 +797,7 @@ impl BitRelations> for ChunkedBitSet { // If we reach here, `self_chunk_words` is definitely changing. let self_chunk_words = Rc::make_mut(self_chunk_words); - let has_changed = bitwise( + let has_changed = update_words( &mut self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -865,7 +865,7 @@ impl BitRelations> for ChunkedBitSet { // See `ChunkedBitSet::union` for details on what is happening here. let num_words = num_words(*chunk_domain_size as usize); let op = |a: Word, b: Word| a & !b; - if !bitwise_changes( + if !would_modify_words( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -874,7 +874,7 @@ impl BitRelations> for ChunkedBitSet { } let self_chunk_words = Rc::make_mut(self_chunk_words); - let has_changed = bitwise( + let has_changed = update_words( &mut self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -914,7 +914,7 @@ impl BitRelations> for ChunkedBitSet { // See `ChunkedBitSet::union` for details on what is happening here. let num_words = num_words(*chunk_domain_size as usize); let op = |a, b| a & b; - if !bitwise_changes( + if !would_modify_words( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -923,7 +923,7 @@ impl BitRelations> for ChunkedBitSet { } let self_chunk_words = Rc::make_mut(self_chunk_words); - let has_changed = bitwise( + let has_changed = update_words( &mut self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -1052,10 +1052,10 @@ impl fmt::Debug for ChunkedBitSet { } } -/// Sets `out_vec[i] = op(out_vec[i], in_vec[i])` for each index `i` in both +/// Sets `lhs[i] = op(lhs[i], rhs[i])` for each index `i` in both /// slices. The slices must have the same length. /// -/// Returns true if at least one bit in `out_vec` was changed. +/// Returns true if at least one bit in `lhs` was changed. /// /// ## Warning /// Some bitwise operations (e.g. union-not, xor) can set output bits that were @@ -1065,16 +1065,16 @@ impl fmt::Debug for ChunkedBitSet { /// "changed" return value unreliable, because the change might have only /// affected excess bits. #[inline] -fn bitwise(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool +fn update_words(lhs: &mut [Word], rhs: &[Word], op: Op) -> bool where Op: Fn(Word, Word) -> Word, { - assert_eq!(out_vec.len(), in_vec.len()); + assert_eq!(lhs.len(), rhs.len()); let mut changed = 0; - for (out_elem, in_elem) in iter::zip(out_vec, in_vec) { - let old_val = *out_elem; - let new_val = op(old_val, *in_elem); - *out_elem = new_val; + for (lhs_slot, &rhs_val) in iter::zip(lhs, rhs) { + let old_val = *lhs_slot; + let new_val = op(old_val, rhs_val); + *lhs_slot = new_val; // This is essentially equivalent to a != with changed being a bool, but // in practice this code gets auto-vectorized by the compiler for most // operators. Using != here causes us to generate quite poor code as the @@ -1084,21 +1084,40 @@ where changed != 0 } -/// Does this bitwise operation change `out_vec`? +/// Returns true if a call to [`update_words`] would modify `lhs`, i.e. +/// `lhs[i] != op(lhs[i], rhs[i])` for some `i`. #[inline] -fn bitwise_changes(out_vec: &[Word], in_vec: &[Word], op: Op) -> bool +fn would_modify_words(lhs: &[Word], rhs: &[Word], op: Op) -> bool where Op: Fn(Word, Word) -> Word, { - assert_eq!(out_vec.len(), in_vec.len()); - for (out_elem, in_elem) in iter::zip(out_vec, in_vec) { - let old_val = *out_elem; - let new_val = op(old_val, *in_elem); - if old_val != new_val { + assert_eq!(lhs.len(), rhs.len()); + + // To make codegen more vectorizer-friendly, we traverse each slice in larger + // "subchunks", and only consider an early return at subchunk boundaries. + // These subchunks are smaller than full `ChunkedBitSet` chunks, so that + // we still have some chance of stopping early. + const SUBCHUNK_LEN: usize = 64 / size_of::(); + let (lhs_chunks, lhs_tail) = lhs.as_chunks::(); + let (rhs_chunks, rhs_tail) = rhs.as_chunks::(); + + let would_modify_subchunk = |lhs_chunk: &[Word], rhs_chunk: &[Word]| { + let mut changed = 0; + for (&old_val, &rhs_val) in iter::zip(lhs_chunk, rhs_chunk) { + let new_val = op(old_val, rhs_val); + // Set `changed` to a non-zero value if any bits changed. + // This gives better SIMD codegen than using an actual boolean. + changed |= old_val ^ new_val; + } + changed != 0 + }; + + for (lhs_chunk, rhs_chunk) in iter::zip(lhs_chunks, rhs_chunks) { + if would_modify_subchunk(lhs_chunk, rhs_chunk) { return true; } } - false + would_modify_subchunk(lhs_tail, rhs_tail) } /// A bitset with a mixed representation, using `DenseBitSet` for small and @@ -1499,7 +1518,7 @@ impl BitMatrix { assert!(write.index() < self.num_rows); assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); - bitwise(&mut self.words[write_start..write_end], &with.words, |a, b| a | b) + update_words(&mut self.words[write_start..write_end], &with.words, |a, b| a | b) } /// Sets every cell in `row` to true. diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 5364a4141805b..2f257bb092ae8 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -38,7 +38,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { (tcx.hir_attrs(id.hir_id)[id.attr_index as usize].id(), id.lint_index) } }; - (attr_id, lint_index.expect("fulfilled expectations must have a lint index")) + (attr_id, lint_index) }; let fulfilled_expectations: FxHashSet<_> = diff --git a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs new file mode 100644 index 0000000000000..699004a3b37e0 --- /dev/null +++ b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs @@ -0,0 +1,79 @@ +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer + /// and a pointer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(fuzzy_provenance_casts)] + /// + /// fn main() { + /// let _dangling = 16_usize as *const u8; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting an integer to a pointer is considered bad style, as a pointer + /// contains, besides the *address* also a *provenance*, indicating what + /// memory the pointer is allowed to read/write. Casting an integer, which + /// doesn't have provenance, to a pointer requires the compiler to assign + /// (guess) provenance. The compiler assigns "all exposed valid" (see the + /// docs of [`ptr::with_exposed_provenance`] for more information about this + /// "exposing"). This penalizes the optimiser and is not well suited for + /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI + /// platforms). + /// + /// It is much better to use [`ptr::with_addr`] instead to specify the + /// provenance you want. If using this function is not possible because the + /// code relies on exposed provenance then there is as an escape hatch + /// [`ptr::with_exposed_provenance`]. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr + /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html + pub FUZZY_PROVENANCE_CASTS, + Allow, + "a fuzzy integer to pointer cast is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for `as` casts between an integer and a pointer. + FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + // Only lint casts from integer to pointer + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if !cast_from_ty.is_integral() { + return; + } + let cast_to_ty = typeck_results.expr_ty(expr); + if !cast_to_ty.is_raw_ptr() { + return; + } + + let sugg = + expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion { + lo: cast_from_expr.span.shrink_to_lo(), + hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), + }); + let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) + } +} diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 65dfa8b93de78..76d55030b1312 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -361,9 +361,9 @@ where // have no uncaptured args, then we should warn to the user that // it's redundant to capture all args explicitly. if new_capture_rules - && let Some((captured_args, capturing_span)) = - opaque.bounds.iter().find_map(|bound| match *bound { - hir::GenericBound::Use(a, s) => Some((a, s)), + && let Some((use_idx, captured_args, capturing_span)) = + opaque.bounds.iter().enumerate().find_map(|(i, bound)| match *bound { + hir::GenericBound::Use(a, s) => Some((i, a, s)), _ => None, }) { @@ -400,11 +400,25 @@ where .iter() .all(|(def_id, _)| explicitly_captured.contains(def_id)) { + // Extend the removal span to include the `+` joiner adjacent + // to `use<...>`, so applying the suggestion does not leave + // behind a stray `+` that fails to parse. + let suggestion_span = if let Some(next) = opaque.bounds.get(use_idx + 1) { + capturing_span.with_hi(next.span().lo()) + } else if let Some(prev_idx) = use_idx.checked_sub(1) { + let prev = opaque.bounds[prev_idx]; + capturing_span.with_lo(prev.span().hi()) + } else { + // `impl use<...>` with no other bound is not valid + // syntax, so this branch is unreachable in practice. + capturing_span + }; + self.tcx.emit_node_span_lint( IMPL_TRAIT_REDUNDANT_CAPTURES, self.tcx.local_def_id_to_hir_id(opaque_def_id), opaque_span, - ImplTraitRedundantCapturesLint { capturing_span }, + ImplTraitRedundantCapturesLint { capturing_span: suggestion_span }, ); } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index b8da46594cfc2..00ae385c57672 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -233,7 +233,7 @@ pub trait LintLevelsProvider { &self, attr_id: AttrId, attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId; } @@ -258,7 +258,7 @@ impl LintLevelsProvider for TopDown { &self, attr_id: AttrId, _attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId { UnstableLintExpectationId { attr_id, lint_index } } @@ -296,7 +296,7 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { &self, _attr_id: AttrId, attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId { let attr_index = attr_index.try_into().unwrap(); StableLintExpectationId { hir_id: self.cur, attr_index, lint_index } @@ -740,11 +740,7 @@ where // `Expect` is the only lint level with a `LintExpectationId` that can be created // from an attribute. let lint_id = (level == Level::Expect).then(|| { - self.provider.mk_lint_expectation_id( - attr.id(), - attr_index, - Some(lint_index as u16), - ) + self.provider.mk_lint_expectation_id(attr.id(), attr_index, lint_index as u16) }); let sp = li.span(); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 10fd1d1501b3b..1efc8b70ef22d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -45,6 +45,7 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; mod function_cast_as_integer; +mod fuzzy_provenance_casts; mod gpukernel_abi; mod if_let_rescope; mod impl_trait_overcaptures; @@ -56,6 +57,7 @@ mod let_underscore; mod levels; pub mod lifetime_syntax; mod lints; +mod lossy_provenance_casts; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod multiple_supertrait_upcastable; @@ -92,6 +94,7 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use function_cast_as_integer::*; +use fuzzy_provenance_casts::FuzzyProvenanceCasts; use gpukernel_abi::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; @@ -100,6 +103,7 @@ use internal::*; use invalid_from_utf8::*; use let_underscore::*; use lifetime_syntax::*; +use lossy_provenance_casts::LossyProvenanceCasts; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -250,6 +254,8 @@ late_lint_methods!( CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, + FuzzyProvenanceCasts: FuzzyProvenanceCasts, + LossyProvenanceCasts: LossyProvenanceCasts, ] ] ); @@ -644,6 +650,10 @@ fn register_builtins(store: &mut LintStore) { ); store.register_removed("wasm_c_abi", "the wasm C ABI has been fixed"); store.register_removed("soft_unstable", "the general soft-unstable mechanism has been removed"); + store.register_removed( + "inline_always_mismatching_target_features", + "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 091b7ac228b54..c77e70dcbe9bb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2932,3 +2932,85 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { #[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] #[note("this method was used to add checks to the `Eq` derive macro")] pub(crate) struct EqInternalMethodImplemented; + +#[derive(Diagnostic)] +#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] +#[help( + "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" +)] +pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", + applicability = "has-placeholders" +)] +pub(crate) struct LossyProvenanceInt2PtrSuggestion { + #[suggestion_part(code = "(...).with_addr(")] + pub lo: Span, + #[suggestion_part(code = ")")] + pub hi: Span, +} + +#[derive(Diagnostic)] +#[diag( + "under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`" +)] +#[help( + "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" +)] +pub(crate) struct LossyProvenancePtr2Int<'tcx> { + pub cast_from_ty: Ty<'tcx>, + pub cast_to_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: Option>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { + #[multipart_suggestion( + "use `.addr()` to obtain the address of a pointer", + applicability = "maybe-incorrect" + )] + NeedsParensCast { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr() as {cast_to_ty}")] + cast_span: Span, + cast_to_ty: Ty<'tcx>, + }, + #[multipart_suggestion( + "use `.addr()` to obtain the address of a pointer", + applicability = "maybe-incorrect" + )] + NeedsParens { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr()")] + cast_span: Span, + }, + #[suggestion( + "use `.addr()` to obtain the address of a pointer", + code = ".addr() as {cast_to_ty}", + applicability = "maybe-incorrect" + )] + NeedsCast { + #[primary_span] + cast_span: Span, + cast_to_ty: Ty<'tcx>, + }, + #[suggestion( + "use `.addr()` to obtain the address of a pointer", + code = ".addr()", + applicability = "maybe-incorrect" + )] + Other { + #[primary_span] + cast_span: Span, + }, +} diff --git a/compiler/rustc_lint/src/lossy_provenance_casts.rs b/compiler/rustc_lint/src/lossy_provenance_casts.rs new file mode 100644 index 0000000000000..bdb5bdf6bb0e3 --- /dev/null +++ b/compiler/rustc_lint/src/lossy_provenance_casts.rs @@ -0,0 +1,98 @@ +use rustc_ast::util::parser::ExprPrecedence; +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer + /// and an integer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(lossy_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let _addr: usize = &x as *const u8 as usize; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting a pointer to an integer is a lossy operation, because beyond + /// just an *address* a pointer may be associated with a particular + /// *provenance*. This information is used by the optimiser and for dynamic + /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). + /// + /// Since this cast is lossy, it is considered good style to use the + /// [`ptr::addr`] method instead, which has a similar effect, but doesn't + /// "expose" the pointer provenance. This improves optimisation potential. + /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information + /// about exposing pointer provenance. + /// + /// If your code can't comply with strict provenance and needs to expose + /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, + /// which preserves the behaviour of `as usize` casts while being explicit + /// about the semantics. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr + /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance + pub LOSSY_PROVENANCE_CASTS, + Allow, + "a lossy pointer to integer cast is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for `as` casts between a pointer and an integer. + LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + // Only lint casts from pointer to integer + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if !cast_from_ty.is_raw_ptr() { + return; + } + let cast_to_ty = typeck_results.expr_ty(expr); + if !cast_to_ty.is_integral() { + return; + } + + let sugg = expr.span.can_be_used_for_suggestions().then(|| { + let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; + let needs_cast = !cast_to_ty.is_usize(); + let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); + let expr_span = cast_from_expr.span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast { + expr_span, + cast_span, + cast_to_ty, + }, + (true, false) => { + LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } + } + (false, true) => { + LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty } + } + (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span }, + } + }); + + let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index c6892419443f8..caa41fd0f6ab3 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -51,12 +51,10 @@ declare_lint_pass! { FLOAT_LITERAL_F32_FALLBACK, FORBIDDEN_LINT_GROUPS, FUNCTION_ITEM_REFERENCES, - FUZZY_PROVENANCE_CASTS, HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, - INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, @@ -68,7 +66,6 @@ declare_lint_pass! { LINKER_INFO, LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, - LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, MACRO_USE_EXTERN_CRATE, MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -2594,96 +2591,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer - /// and a pointer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(fuzzy_provenance_casts)] - /// - /// fn main() { - /// let _dangling = 16_usize as *const u8; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting an integer to a pointer is considered bad style, as a pointer - /// contains, besides the *address* also a *provenance*, indicating what - /// memory the pointer is allowed to read/write. Casting an integer, which - /// doesn't have provenance, to a pointer requires the compiler to assign - /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::with_exposed_provenance`] for more information about this - /// "exposing"). This penalizes the optimiser and is not well suited for - /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI - /// platforms). - /// - /// It is much better to use [`ptr::with_addr`] instead to specify the - /// provenance you want. If using this function is not possible because the - /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::with_exposed_provenance`]. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html - pub FUZZY_PROVENANCE_CASTS, - Allow, - "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint! { - /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer - /// and an integer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(lossy_provenance_casts)] - /// - /// fn main() { - /// let x: u8 = 37; - /// let _addr: usize = &x as *const u8 as usize; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting a pointer to an integer is a lossy operation, because beyond - /// just an *address* a pointer may be associated with a particular - /// *provenance*. This information is used by the optimiser and for dynamic - /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). - /// - /// Since this cast is lossy, it is considered good style to use the - /// [`ptr::addr`] method instead, which has a similar effect, but doesn't - /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information - /// about exposing pointer provenance. - /// - /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, - /// which preserves the behaviour of `as usize` casts while being explicit - /// about the semantics. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance - pub LOSSY_PROVENANCE_CASTS, - Allow, - "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance_lints; -} - declare_lint! { /// The `const_evaluatable_unchecked` lint detects a generic constant used /// in a type. @@ -5492,61 +5399,6 @@ declare_lint! { "detects tail calls of functions marked with `#[track_caller]`", @feature_gate = explicit_tail_calls; } -declare_lint! { - /// The `inline_always_mismatching_target_features` lint will trigger when a - /// function with the `#[inline(always)]` and `#[target_feature(enable = "...")]` - /// attributes is called and cannot be inlined due to missing target features in the caller. - /// - /// ### Example - /// - /// ```rust,ignore (fails on x86_64) - /// #[inline(always)] - /// #[target_feature(enable = "fp16")] - /// unsafe fn callee() { - /// // operations using fp16 types - /// } - /// - /// // Caller does not enable the required target feature - /// fn caller() { - /// unsafe { callee(); } - /// } - /// - /// fn main() { - /// caller(); - /// } - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: call to `#[inline(always)]`-annotated `callee` requires the same target features. Function will not have `alwaysinline` attribute applied - /// --> $DIR/builtin.rs:5192:14 - /// | - /// 10 | unsafe { callee(); } - /// | ^^^^^^^^ - /// | - /// note: `fp16` target feature enabled in `callee` here but missing from `caller` - /// --> $DIR/builtin.rs:5185:1 - /// | - /// 3 | #[target_feature(enable = "fp16")] - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// 4 | unsafe fn callee() { - /// | ------------------ - /// = note: `#[warn(inline_always_mismatching_target_features)]` on by default - /// warning: 1 warning emitted - /// ``` - /// - /// ### Explanation - /// - /// Inlining a function with a target feature attribute into a caller that - /// lacks the corresponding target feature can lead to unsound behavior. - /// LLVM may select the wrong instructions or registers, or reorder - /// operations, potentially resulting in runtime errors. - pub INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, - Warn, - r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#, -} - declare_lint! { /// The `repr_c_enums_larger_than_int` lint detects `repr(C)` enums with discriminant /// values that do not fit into a C `int` or `unsigned int`. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index fc8b2b65a8d48..1edc12e1ca7eb 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -109,7 +109,7 @@ pub enum LintExpectationId { #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)] pub struct UnstableLintExpectationId { pub attr_id: AttrId, - pub lint_index: Option, + pub lint_index: u16, } impl From for LintExpectationId { @@ -125,7 +125,7 @@ impl From for LintExpectationId { pub struct StableLintExpectationId { pub hir_id: HirId, pub attr_index: u16, - pub lint_index: Option, + pub lint_index: u16, } impl StableHash for StableLintExpectationId { @@ -135,7 +135,7 @@ impl StableHash for StableLintExpectationId { hir_id.stable_hash(hcx, hasher); attr_index.stable_hash(hcx, hasher); - lint_index.expect("must be filled to call `stable_hash`").stable_hash(hcx, hasher); + lint_index.stable_hash(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index d62e4ec941d21..f9b6bf02ad7c4 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2710,12 +2710,6 @@ rustc_queries! { desc { "monomorphization-time checking" } } - /// Builds the set of functions that should be skipped for the move-size check. - query skip_move_check_fns(_: ()) -> &'tcx FxIndexSet { - arena_cache - desc { "functions to skip for move-size check" } - } - query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned>], &'tcx [Spanned>]), NormalizationErrorInMono> { desc { "collecting items used by `{}`", key.0 } cache_on_disk diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index ff7e518f91a81..e92f74722626b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -583,6 +583,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } | ExprKind::ByUse { .. } + // A reborrow is an rvalue. If a place is needed for it, materialize + // the rvalue in a temporary instead of treating the reborrow + // expression itself as an assignable place. + | ExprKind::Reborrow { .. } | ExprKind::WrapUnsafeBinder { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); @@ -590,12 +594,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) } - ExprKind::Reborrow { .. } => { - // FIXME(reborrow): it should currently be impossible to end up evaluating a - // Reborrow expression as a place. That might not in the future, but what this then - // evaluates to requires further thought. - unreachable!(); - } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index eb6a0754358d1..1a2f0a791b697 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -43,8 +43,7 @@ impl Category { | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } | ExprKind::PlaceUnwrapUnsafeBinder { .. } - | ExprKind::ValueUnwrapUnsafeBinder { .. } - | ExprKind::Reborrow { .. } => Some(Category::Place), + | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } @@ -70,6 +69,10 @@ impl Category { | ExprKind::Repeat { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } + // A reborrow expression produces a value represented in MIR as + // `Rvalue::Reborrow`. Its source may be a place, but the reborrow + // expression itself does not denote an assignable place. + | ExprKind::Reborrow { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), diff --git a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs b/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs deleted file mode 100644 index abad28f0a8f83..0000000000000 --- a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs +++ /dev/null @@ -1,88 +0,0 @@ -use rustc_hir::attrs::InlineAttr; -use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; -use rustc_middle::mir::{Body, TerminatorKind}; -use rustc_middle::ty::{self, TyCtxt}; - -use crate::pass_manager::MirLint; - -pub(super) struct CheckInlineAlwaysTargetFeature; - -impl<'tcx> MirLint<'tcx> for CheckInlineAlwaysTargetFeature { - fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - check_inline_always_target_features(tcx, body) - } -} - -/// `#[target_feature]`-annotated functions can be marked `#[inline]` and will only be inlined if -/// the target features match (as well as all of the other inlining heuristics). `#[inline(always)]` -/// will always inline regardless of matching target features, which can result in errors from LLVM. -/// However, it is desirable to be able to always annotate certain functions (e.g. SIMD intrinsics) -/// as `#[inline(always)]` but check the target features match in Rust to avoid the LLVM errors. -/// -/// We check the caller and callee target features to ensure that this can -/// be done or emit a lint. -#[inline] -fn check_inline_always_target_features<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let caller_def_id = body.source.def_id().expect_local(); - if !tcx.def_kind(caller_def_id).has_codegen_attrs() { - return; - } - - let caller_codegen_fn_attrs = tcx.codegen_fn_attrs(caller_def_id); - - for bb in body.basic_blocks.iter() { - let terminator = bb.terminator(); - match &terminator.kind { - TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { - let fn_ty = func.ty(body, tcx); - let ty::FnDef(callee_def_id, _) = *fn_ty.kind() else { - continue; - }; - - if !tcx.def_kind(callee_def_id).has_codegen_attrs() { - continue; - } - let callee_codegen_fn_attrs = tcx.codegen_fn_attrs(callee_def_id); - if callee_codegen_fn_attrs.inline != InlineAttr::Always - || callee_codegen_fn_attrs.target_features.is_empty() - { - continue; - } - - // Scan the users defined target features and ensure they - // match the caller. - if tcx.is_target_feature_call_safe( - &callee_codegen_fn_attrs.target_features, - &caller_codegen_fn_attrs - .target_features - .iter() - .cloned() - .chain(tcx.sess.target_features.iter().map(|feat| TargetFeature { - name: *feat, - kind: TargetFeatureKind::Implied, - })) - .collect::>(), - ) { - continue; - } - - let callee_only: Vec<_> = callee_codegen_fn_attrs - .target_features - .iter() - .filter(|it| !caller_codegen_fn_attrs.target_features.contains(it)) - .filter(|it| !matches!(it.kind, TargetFeatureKind::Implied)) - .map(|it| it.name.as_str()) - .collect(); - - crate::errors::emit_inline_always_target_feature_diagnostic( - tcx, - terminator.source_info.span, - callee_def_id, - caller_def_id.into(), - &callee_only, - ); - } - _ => (), - } - } -} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 39c85489f939a..e9853abee406c 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -5,52 +5,11 @@ use rustc_errors::{ }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; -use rustc_middle::query::QueryKey; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; use rustc_span::{Ident, Span, Symbol}; -/// Emit diagnostic for calls to `#[inline(always)]`-annotated functions with a -/// `#[target_feature]` attribute where the caller enables a different set of target features. -pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - call_span: Span, - callee_def_id: DefId, - caller_def_id: DefId, - callee_only: &[&'a str], -) { - tcx.emit_node_span_lint( - lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, - tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()), - call_span, - rustc_errors::DiagDecorator(|lint| { - let callee = tcx.def_path_str(callee_def_id); - let caller = tcx.def_path_str(caller_def_id); - - lint.primary_message(format!( - "call to `#[inline(always)]`-annotated `{callee}` \ - requires the same target features to be inlined" - )); - lint.note("function will not be inlined"); - - lint.note(format!( - "the following target features are on `{callee}` but missing from `{caller}`: {}", - callee_only.join(", ") - )); - lint.span_note(callee_def_id.default_span(tcx), format!("`{callee}` is defined here")); - - let feats = callee_only.join(","); - lint.span_suggestion( - tcx.def_span(caller_def_id).shrink_to_lo(), - format!("add `#[target_feature]` attribute to `{caller}`"), - format!("#[target_feature(enable = \"{feats}\")]\n"), - lint::Applicability::MaybeIncorrect, - ); - }), - ); -} - #[derive(Diagnostic)] #[diag("function cannot return without recursing")] #[help("a `loop` may express intention better if this is on purpose")] @@ -290,7 +249,7 @@ pub(crate) enum UnusedVariableSugg { shorthands: Vec, #[suggestion_part(code = "_")] non_shorthands: Vec, - name: Symbol, + name: String, }, #[multipart_suggestion( diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 17c5ee110b86a..310604207f8de 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -122,7 +122,6 @@ declare_passes! { mod add_subtyping_projections : Subtyper; mod check_inline : CheckForceInline; mod check_call_recursion : CheckCallRecursion, CheckDropRecursion; - mod check_inline_always_target_features: CheckInlineAlwaysTargetFeature; mod check_alignment : CheckAlignment; mod check_enums : CheckEnums; mod check_const_item_mutation : CheckConstItemMutation; @@ -403,9 +402,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { // MIR-level lints. &Lint(check_inline::CheckForceInline), &Lint(check_call_recursion::CheckCallRecursion), - // Check callee's target features match callers target features when - // using `#[inline(always)]` - &Lint(check_inline_always_target_features::CheckInlineAlwaysTargetFeature), &Lint(check_packed_ref::CheckPackedRef), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index a1b2a2853c0bd..c449cf6867395 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1068,7 +1068,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let sugg = if any_shorthand { errors::UnusedVariableSugg::TryIgnore { - name, + name: name.to_ident_string(), shorthands: introductions .iter() .filter_map( diff --git a/compiler/rustc_monomorphize/src/mono_checks/mod.rs b/compiler/rustc_monomorphize/src/mono_checks/mod.rs index 1ecda824fb8c2..6569eeafec17d 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/mod.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/mod.rs @@ -15,9 +15,5 @@ fn check_mono_item<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { } pub(super) fn provide(providers: &mut Providers) { - *providers = Providers { - check_mono_item, - skip_move_check_fns: move_check::skip_move_check_fns, - ..*providers - } + *providers = Providers { check_mono_item, ..*providers } } diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index a24b0443d39c9..af03e2a0d2d66 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -1,12 +1,12 @@ use rustc_abi::Size; -use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Location, traversal}; -use rustc_middle::ty::{self, AssocTag, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; -use rustc_span::{Ident, Span, Spanned, sym}; +use rustc_span::{Span, Spanned, sym}; use tracing::{debug, trace}; use crate::errors::LargeAssignmentsLint; @@ -98,7 +98,7 @@ impl<'tcx> MoveCheckVisitor<'tcx> { let ty::FnDef(def_id, _) = *callee_ty.kind() else { return; }; - if self.tcx.skip_move_check_fns(()).contains(&def_id) { + if should_skip_fn(self.tcx, def_id) { return; } @@ -188,29 +188,18 @@ impl<'tcx> MoveCheckVisitor<'tcx> { } } -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { - for &impl_def_id in tcx.inherent_impls(def_id) { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( - tcx, - fn_ident, - AssocTag::Fn, - def_id, - ) { - return Some(new.def_id); - } +/// Return `true` if `def_id` is `Box::new`, `Rc::new` or `Arc::new`. +fn should_skip_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let DefKind::AssocFn = tcx.def_kind(def_id) + && tcx.item_name(def_id) == sym::new + && let parent = tcx.parent(def_id) + && let DefKind::Impl { of_trait: false } = tcx.def_kind(parent) + && let ty::Adt(adt_def, ..) = + tcx.type_of(parent).instantiate_identity().skip_normalization().kind() + { + return Some(adt_def.did()) == tcx.lang_items().owned_box() + || Some(adt_def.did()) == tcx.get_diagnostic_item(sym::Rc) + || Some(adt_def.did()) == tcx.get_diagnostic_item(sym::Arc); } - None -} - -pub(crate) fn skip_move_check_fns(tcx: TyCtxt<'_>, _: ()) -> FxIndexSet { - let fns = [ - (tcx.lang_items().owned_box(), "new"), - (tcx.get_diagnostic_item(sym::Rc), "new"), - (tcx.get_diagnostic_item(sym::Arc), "new"), - ]; - fns.into_iter() - .filter_map(|(def_id, fn_name)| { - def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) - }) - .collect() + false } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 12dc35d29ad9f..a0b6321a6c437 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1287,6 +1287,26 @@ pub(crate) struct IncorrectImplRestriction { pub inner_str: String, } +#[derive(Diagnostic)] +#[diag("incorrect `mut` restriction")] +#[help( + "some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path" +)] +pub(crate) struct IncorrectMutRestriction { + #[primary_span] + #[suggestion( + "help: use `in` to restrict mutations to the path `{$inner_str}`", + code = "in {inner_str}", + applicability = "machine-applicable" + )] + pub span: Span, + pub inner_str: String, +} + #[derive(Diagnostic)] #[diag(" ... else {\"{\"} ... {\"}\"} is not allowed")] pub(crate) struct AssignmentElseNotAllowed { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 68fda3b86ddcb..bed03800d3f4c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2108,6 +2108,7 @@ impl<'a> Parser<'a> { return Err(err); } }; + let mut_restriction = p.parse_mut_restriction()?; // Unsafe fields are not supported in tuple structs, as doing so would result in a // parsing ambiguity for `struct X(unsafe fn())`. let ty = match p.parse_ty() { @@ -2140,6 +2141,7 @@ impl<'a> Parser<'a> { FieldDef { span: lo.to(ty.span), vis, + mut_restriction, safety: Safety::Default, ident: None, id: DUMMY_NODE_ID, @@ -2164,9 +2166,18 @@ impl<'a> Parser<'a> { self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; + let mut_restriction = this.parse_mut_restriction()?; let safety = this.parse_unsafe_field(); - this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs, ident_span) - .map(|field| (field, Trailing::No, UsePreAttrPos::No)) + this.parse_single_struct_field( + adt_ty, + lo, + vis, + mut_restriction, + safety, + attrs, + ident_span, + ) + .map(|field| (field, Trailing::No, UsePreAttrPos::No)) }) } @@ -2176,11 +2187,12 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + mut_restriction: MutRestriction, safety: Safety, attrs: AttrVec, ident_span: Span, ) -> PResult<'a, FieldDef> { - let a_var = self.parse_name_and_ty(adt_ty, lo, vis, safety, attrs)?; + let a_var = self.parse_name_and_ty(adt_ty, lo, vis, mut_restriction, safety, attrs)?; match self.token.kind { token::Comma => { self.bump(); @@ -2299,6 +2311,7 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + mut_restriction: MutRestriction, safety: Safety, attrs: AttrVec, ) -> PResult<'a, FieldDef> { @@ -2337,6 +2350,7 @@ impl<'a> Parser<'a> { ident: Some(name), vis, safety, + mut_restriction, id: DUMMY_NODE_ID, ty, default, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 850d64c8ecf44..a16ddb34b3c2e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::util::classify; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction, - MgcaDisambiguation, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, - VisibilityKind, + MgcaDisambiguation, MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, + Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -51,8 +51,8 @@ pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType}; use tracing::debug; use crate::errors::{ - self, IncorrectImplRestriction, IncorrectVisibilityRestriction, NonStringAbiLiteral, - TokenDescription, + self, IncorrectImplRestriction, IncorrectMutRestriction, IncorrectVisibilityRestriction, + NonStringAbiLiteral, TokenDescription, }; use crate::exp; @@ -297,6 +297,13 @@ impl SeqSep { } } +/// Whether parsing `impl` or `mut` restrictions. +#[derive(Clone, Copy, Debug)] +enum ParsingRestrictionKind { + Impl, + Mut, +} + #[derive(Debug)] pub enum FollowedByType { Yes, @@ -1550,37 +1557,9 @@ impl<'a> Parser<'a> { /// Enforces the `impl_restriction` feature gate whenever an explicit restriction is encountered. fn parse_impl_restriction(&mut self) -> PResult<'a, ImplRestriction> { if self.eat_keyword(exp!(Impl)) { - let lo = self.prev_token.span; - // No units or tuples are allowed to follow `impl` here, so we can safely bump `(`. - self.expect(exp!(OpenParen))?; - if self.eat_keyword(exp!(In)) { - let path = self.parse_path(PathStyle::Mod)?; // `in path` - self.expect(exp!(CloseParen))?; // `)` - let restriction = RestrictionKind::Restricted { - path: Box::new(path), - id: ast::DUMMY_NODE_ID, - shorthand: false, - }; - let span = lo.to(self.prev_token.span); - self.psess.gated_spans.gate(sym::impl_restriction, span); - return Ok(ImplRestriction { kind: restriction, span, tokens: None }); - } else if self.look_ahead(1, |t| t == &token::CloseParen) - && self.is_keyword_ahead(0, &[kw::Crate, kw::Super, kw::SelfLower]) - { - let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` - self.expect(exp!(CloseParen))?; // `)` - let restriction = RestrictionKind::Restricted { - path: Box::new(path), - id: ast::DUMMY_NODE_ID, - shorthand: true, - }; - let span = lo.to(self.prev_token.span); - self.psess.gated_spans.gate(sym::impl_restriction, span); - return Ok(ImplRestriction { kind: restriction, span, tokens: None }); - } else { - self.recover_incorrect_impl_restriction(lo)?; - // Emit diagnostic, but continue with no impl restriction. - } + let (kind, span, gated_span) = self.parse_restriction(ParsingRestrictionKind::Impl)?; + self.psess.gated_spans.gate(sym::impl_restriction, gated_span); + return Ok(ImplRestriction { kind, span, tokens: None }); } Ok(ImplRestriction { kind: RestrictionKind::Unrestricted, @@ -1589,15 +1568,73 @@ impl<'a> Parser<'a> { }) } - /// Recovery for e.g. `impl(something) trait` - fn recover_incorrect_impl_restriction(&mut self, lo: Span) -> PResult<'a, ()> { - let path = self.parse_path(PathStyle::Mod)?; - self.expect(exp!(CloseParen))?; // `)` - let path_str = pprust::path_to_string(&path); - self.dcx().emit_err(IncorrectImplRestriction { span: path.span, inner_str: path_str }); - let end = self.prev_token.span; - self.psess.gated_spans.gate(sym::impl_restriction, lo.to(end)); - Ok(()) + /// Parses an optional `mut` restriction. + /// Enforces the `mut_restriction` feature gate whenever an explicit restriction is encountered. + fn parse_mut_restriction(&mut self) -> PResult<'a, MutRestriction> { + if self.eat_keyword(exp!(Mut)) { + let (kind, span, gated_span) = self.parse_restriction(ParsingRestrictionKind::Mut)?; + self.psess.gated_spans.gate(sym::mut_restriction, gated_span); + return Ok(MutRestriction { kind, span, tokens: None }); + } + Ok(MutRestriction { + kind: RestrictionKind::Unrestricted, + span: self.token.span.shrink_to_lo(), + tokens: None, + }) + } + + /// Parses `impl` or `mut` restrictions. + /// Returns the parsed restriction and its span, as well as the gated span. + fn parse_restriction( + &mut self, + restriction_kind: ParsingRestrictionKind, + ) -> PResult<'a, (RestrictionKind, Span, Span)> { + let lo = self.prev_token.span; + // No units or tuples are allowed to follow `impl` or `mut` here, so we can safely bump `(`. + self.expect(exp!(OpenParen))?; + if self.eat_keyword(exp!(In)) { + let path = self.parse_path(PathStyle::Mod)?; // `in path` + self.expect(exp!(CloseParen))?; // `)` + let restriction = RestrictionKind::Restricted { + path: Box::new(path), + id: ast::DUMMY_NODE_ID, + shorthand: false, + }; + let span = lo.to(self.prev_token.span); + Ok((restriction, span, span)) + } else if self.look_ahead(1, |t| t == &token::CloseParen) + && self.is_keyword_ahead(0, &[kw::Crate, kw::Super, kw::SelfLower]) + { + let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` + self.expect(exp!(CloseParen))?; // `)` + let restriction = RestrictionKind::Restricted { + path: Box::new(path), + id: ast::DUMMY_NODE_ID, + shorthand: true, + }; + let span = lo.to(self.prev_token.span); + Ok((restriction, span, span)) + } else { + // Emit diagnostic, but continue with no restrictions. + // Recovery for `impl(something) trait` or `mut (something) field`. + let path = self.parse_path(PathStyle::Mod)?; + self.expect(exp!(CloseParen))?; // `)` + let path_str = pprust::path_to_string(&path); + let end = self.prev_token.span; + match restriction_kind { + ParsingRestrictionKind::Impl => { + self.dcx().emit_err(IncorrectImplRestriction { + span: path.span, + inner_str: path_str, + }); + } + ParsingRestrictionKind::Mut => { + self.dcx() + .emit_err(IncorrectMutRestriction { span: path.span, inner_str: path_str }); + } + } + Ok((RestrictionKind::Unrestricted, self.token.span.shrink_to_lo(), lo.to(end))) + } } /// Parses `extern string_literal?`. diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 20f4646490a6b..9e1281fb82cdc 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -750,15 +750,15 @@ impl<'input> Parser<'input> { spec } - /// Always returns an empty `FormatSpec` + /// Always returns an empty `FormatSpec`, except for the `ty` and `ty_span` fields. fn diagnostic(&mut self) -> FormatSpec<'input> { let mut spec = FormatSpec::default(); - let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else { + let Some((Range { start, .. }, _)) = self.consume_pos(':') else { return spec; }; - spec.ty = self.string(start_idx); + spec.ty = self.string(self.input_vec_index); spec.ty_span = { let end = self.input_vec_index2range(self.input_vec_index).start; Some(start..end) diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index a6c7e1890abd1..de6046ec8b9dd 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -567,7 +567,7 @@ fn diagnostic_format_flags() { Argument { position: ArgumentNamed("thing"), position_span: 2..7, - format: FormatSpec { ty: ":blah", ty_span: Some(7..12), ..Default::default() }, + format: FormatSpec { ty: "blah", ty_span: Some(7..12), ..Default::default() }, } ); @@ -588,7 +588,7 @@ fn diagnostic_format_mod() { Argument { position: ArgumentNamed("thing"), position_span: 2..7, - format: FormatSpec { ty: ":+", ty_span: Some(7..9), ..Default::default() }, + format: FormatSpec { ty: "+", ty_span: Some(7..9), ..Default::default() }, } ); diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 6a792e5a803b7..de5c00306a736 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -6,9 +6,9 @@ mod errors; -use std::fmt; use std::marker::PhantomData; use std::ops::ControlFlow; +use std::{debug_assert_matches, fmt}; use errors::{ FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface, @@ -16,14 +16,14 @@ use errors::{ UnnamedItemIsPrivate, }; use rustc_ast::visit::{VisitorResult, try_visit}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::indexmap::IndexSet; use rustc_data_structures::intern::Interned; use rustc_errors::{MultiSpan, listify}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor}; -use rustc_hir::{AmbigArg, ForeignItemId, ItemId, OwnerId, PatKind, find_attr}; +use rustc_hir::{self as hir, AmbigArg, ForeignItemId, ItemId, OwnerId, PatKind, find_attr}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -414,13 +414,76 @@ impl VisibilityLike for EffectiveVisibility { } } +type DefIdsToImpls = FxHashMap>; + +/// Visitor that collects correspondence map between defs and +/// enclosing impls. +struct DefIdsToImplsCollector<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + def_ids_to_impls: &'a mut DefIdsToImpls, + impl_def_id: LocalDefId, +} + +impl<'tcx, 'a> DefIdsToImplsCollector<'tcx, 'a> { + fn collect(tcx: TyCtxt<'tcx>) -> DefIdsToImpls { + let mut def_ids_to_impls = Default::default(); + for item in tcx.hir_free_items() { + let impl_def_id = item.owner_id.def_id; + let DefKind::Impl { of_trait } = tcx.def_kind(impl_def_id) else { + continue; + }; + + // This behavior should mirror `EffectiveVisibility::of_impl::`. + let mut visitor = DefIdsToImplsCollector { + tcx, + impl_def_id, + def_ids_to_impls: &mut def_ids_to_impls, + }; + + visitor.visit(tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip()); + if of_trait { + visitor.visit_trait( + tcx.impl_trait_ref(impl_def_id).instantiate_identity().skip_norm_wip(), + ); + } + } + + def_ids_to_impls + } +} + +impl<'tcx, 'a> DefIdVisitor<'tcx> for DefIdsToImplsCollector<'tcx, 'a> { + const SHALLOW: bool = true; + fn skip_assoc_tys(&self) -> bool { + true + } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) { + if let Some(def_id) = def_id.as_local() { + debug_assert_matches!( + self.tcx.def_kind(def_id), + DefKind::Enum + | DefKind::Union + | DefKind::Struct + | DefKind::ForeignTy + | DefKind::Trait + ); + self.def_ids_to_impls.entry(def_id).or_default().insert(self.impl_def_id); + } + } +} + /// The embargo visitor, used to determine the exports of the AST. struct EmbargoVisitor<'tcx> { tcx: TyCtxt<'tcx>, /// Effective visibilities for reachable nodes. effective_visibilities: EffectiveVisibilities, - /// Has something changed in the level map? - changed: bool, + /// Queue with modified items. + queue: IndexSet, + /// Correspondence between def and impls containing this def. + def_ids_to_impls: DefIdsToImpls, } struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> { @@ -452,12 +515,12 @@ impl<'tcx> EmbargoVisitor<'tcx> { inherited_effective_vis: EffectiveVisibility, max_vis: Option, level: Level, - ) { + ) -> bool { // FIXME(typed_def_id): Make `Visibility::Restricted` use a `LocalModDefId` by default. let private_vis = ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id).into()); if max_vis != Some(private_vis) { - self.changed |= self.effective_visibilities.update( + return self.effective_visibilities.update( def_id, max_vis, private_vis, @@ -466,6 +529,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.tcx, ); } + false } fn reach( @@ -509,11 +573,12 @@ impl<'tcx> EmbargoVisitor<'tcx> { } } - fn check_def_id(&mut self, owner_id: OwnerId) { + fn check_def_id(&mut self, def_id: LocalDefId) { // Update levels of nested things and mark all items // in interfaces of reachable items as reachable. - let item_ev = self.get(owner_id.def_id); - match self.tcx.def_kind(owner_id) { + let item_ev = self.get(def_id); + let def_kind = self.tcx.def_kind(def_id); + match def_kind { // The interface is empty, and no nested items. DefKind::Use | DefKind::ExternCrate | DefKind::GlobalAsm => {} // The interface is empty, and all nested items are processed by `check_def_id`. @@ -526,14 +591,14 @@ impl<'tcx> EmbargoVisitor<'tcx> { | DefKind::Fn | DefKind::TyAlias => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates().ty(); + self.reach(def_id, item_ev).generics().predicates().ty(); } } DefKind::Trait => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); - for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() { + for assoc_item in self.tcx.associated_items(def_id).in_definition_order() { let def_id = assoc_item.def_id.expect_local(); self.update(def_id, item_ev, Level::Reachable); @@ -543,7 +608,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { } DefKind::TraitAlias => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); } } DefKind::Impl { of_trait } => { @@ -558,23 +623,23 @@ impl<'tcx> EmbargoVisitor<'tcx> { // without knowing both "shallow" version of its self type and "shallow" version of // its trait if it exists (which require reaching the `DefId`s in them). let item_ev = EffectiveVisibility::of_impl::( - owner_id.def_id, + def_id, of_trait, self.tcx, &self.effective_visibilities, ); - self.update_eff_vis(owner_id.def_id, item_ev, None, Level::Direct); + self.update_eff_vis(def_id, item_ev, None, Level::Direct); { - let mut reach = self.reach(owner_id.def_id, item_ev); + let mut reach = self.reach(def_id, item_ev); reach.generics().predicates().ty(); if of_trait { reach.trait_ref(); } } - for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() { + for assoc_item in self.tcx.associated_items(def_id).in_definition_order() { let def_id = assoc_item.def_id.expect_local(); let max_vis = if of_trait { None } else { Some(self.tcx.local_visibility(def_id)) }; @@ -587,9 +652,9 @@ impl<'tcx> EmbargoVisitor<'tcx> { } DefKind::Enum => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); } - let def = self.tcx.adt_def(owner_id); + let def = self.tcx.adt_def(def_id); for variant in def.variants() { if let Some(item_ev) = item_ev { self.update(variant.def_id.expect_local(), item_ev, Level::Reachable); @@ -607,19 +672,19 @@ impl<'tcx> EmbargoVisitor<'tcx> { } // Corner case: if the variant is reachable, but its // enum is not, make the enum reachable as well. - self.reach(owner_id.def_id, variant_ev).ty(); + self.reach(def_id, variant_ev).ty(); } if let Some(ctor_def_id) = variant.ctor_def_id() { if let Some(ctor_ev) = self.get(ctor_def_id.expect_local()) { - self.reach(owner_id.def_id, ctor_ev).ty(); + self.reach(def_id, ctor_ev).ty(); } } } } DefKind::Struct | DefKind::Union => { - let def = self.tcx.adt_def(owner_id).non_enum_variant(); + let def = self.tcx.adt_def(def_id).non_enum_variant(); if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); for field in &def.fields { let field = field.did.expect_local(); self.update(field, item_ev, Level::Reachable); @@ -633,7 +698,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.update(ctor_def_id.expect_local(), item_ev, Level::Reachable); } if let Some(ctor_ev) = self.get(ctor_def_id.expect_local()) { - self.reach(owner_id.def_id, ctor_ev).ty(); + self.reach(def_id, ctor_ev).ty(); } } } @@ -653,7 +718,10 @@ impl<'tcx> EmbargoVisitor<'tcx> { | DefKind::ConstParam | DefKind::LifetimeParam | DefKind::Ctor(..) => { - bug!("should be checked while checking parent") + span_bug!( + self.tcx.def_span(def_id), + "{def_kind:?} should be checked while checking parent" + ) } } } @@ -695,6 +763,75 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> { ); self } + + // If a def encountered in the interface is updated, we put those items + // that may be affected by this update into the queue. + fn enqueue_def_id(&mut self, def_id: LocalDefId) { + let def_kind = self.ev.tcx.def_kind(def_id); + match def_kind { + DefKind::Enum + | DefKind::Union + | DefKind::Struct + | DefKind::ForeignTy + | DefKind::Trait => { + self.ev.queue.insert(def_id); + // Make sure that all affected impls are traversed one more time. + if let Some(impls) = self.ev.def_ids_to_impls.get(&def_id) { + // The order in which items are traversed is irrelevant. + #[allow(rustc::potential_query_instability)] + self.ev.queue.extend(impls); + } + } + + DefKind::TraitAlias | DefKind::Fn | DefKind::TyAlias => { + self.ev.queue.insert(def_id); + } + + DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy => { + // FIXME: `EmbargoVisitor` can't check assoc items(see `check_def_id`). + // Let's traverse the whole impl/trait. + self.ev.queue.insert(self.ev.tcx.local_parent(def_id)); + } + + DefKind::Ctor(ctor_of, _) => { + let update_id = match ctor_of { + CtorOf::Struct => self.ev.tcx.local_parent(def_id), + CtorOf::Variant => self.ev.tcx.local_parent(self.ev.tcx.local_parent(def_id)), + }; + // Update the whole ADT. + self.ev.queue.insert(update_id); + } + + // Can be reached via RPIT (impl Fn), but can't affect + // the effective visibility of other defs. + DefKind::Closure => {} + + // Can't be reached + DefKind::Impl { .. } + | DefKind::Field + | DefKind::Variant + | DefKind::Static { .. } + | DefKind::Macro(_) + | DefKind::TyParam + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::SyntheticCoroutineBody + | DefKind::ConstParam + | DefKind::LifetimeParam + | DefKind::Mod + | DefKind::Use + | DefKind::ExternCrate + | DefKind::GlobalAsm + | DefKind::ForeignMod + | DefKind::Const { .. } => { + span_bug!( + self.tcx().def_span(def_id), + "{def_kind:?} unexpectedly reached by `ReachEverythingInTheInterfaceVisitor`" + ) + } + } + } } impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { @@ -708,7 +845,9 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> // produce type privacy errors on any use, so we don't consider it leaked. let max_vis = (self.level != Level::ReachableThroughImplTrait) .then(|| self.ev.tcx.local_visibility(def_id)); - self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level); + if self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level) { + self.enqueue_def_id(def_id); + } } } } @@ -1634,12 +1773,15 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { } fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { + let def_ids_to_impls = DefIdsToImplsCollector::collect(tcx); + // Build up a set of all exported items in the AST. This is a set of all // items which are reachable from external crates based on visibility. let mut visitor = EmbargoVisitor { tcx, effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(), - changed: false, + queue: Default::default(), + def_ids_to_impls, }; visitor.effective_visibilities.check_invariants(tcx); @@ -1692,7 +1834,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { } } - visitor.changed = false; + visitor.queue.clear(); } // FIXME: remove this once proper support for defs reachability from macros is implemented. @@ -1716,18 +1858,14 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { } let crate_items = tcx.hir_crate_items(()); - loop { - for id in crate_items.free_items() { - visitor.check_def_id(id.owner_id); - } - for id in crate_items.foreign_items() { - visitor.check_def_id(id.owner_id); - } - if visitor.changed { - visitor.changed = false; - } else { - break; - } + for id in crate_items.free_items() { + visitor.check_def_id(id.owner_id.def_id); + } + for id in crate_items.foreign_items() { + visitor.check_def_id(id.owner_id.def_id); + } + while let Some(def_id) = visitor.queue.pop() { + visitor.check_def_id(def_id); } visitor.effective_visibilities.check_invariants(tcx); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 21936a0111c62..fb8de90d28aca 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1479,6 +1479,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc ty, is_placeholder: _, default, + mut_restriction: _, safety: _, } = f; walk_list!(self, visit_attribute, attrs); @@ -3912,13 +3913,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { PathSource::Delegation, ); - if let Some(qself) = &delegation.qself { - self.visit_ty(&qself.ty); - } - // Create lifetimes not with `LifetimeRibKind::Generics` but with `LifetimeRibKind::Elided`, - // as we are not processing generic params but generic args in a future function call (#156342). + // as we are not processing generic params but generic args in a future call (#156342, #156758). self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(qself) = &delegation.qself { + this.visit_ty(&qself.ty); + } + this.visit_path(&delegation.path); }); @@ -3937,7 +3938,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { //As we lower target_expr_template body to a body of a function we need a label rib (#148889) this.with_label_rib(RibKind::FnOrCoroutine, |this| { - this.visit_block(body); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.visit_block(body); + }); }); }); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 873aad38a7854..419a47b126980 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3630,6 +3630,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; match use_set { Some(LifetimeUseSet::Many) => {} + // A lifetime bound is a real use of that lifetime parameter, even + // though visiting a bound like `'b: 'a` only records a use of `'a`. + Some(LifetimeUseSet::One { .. }) if !param.bounds.is_empty() => {} Some(LifetimeUseSet::One { use_span, use_ctxt }) => { let param_ident = param.ident; let deletion_span = diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b0df15bfde8bd..f5f4c9e6e2580 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1323,7 +1323,10 @@ impl ExternPreludeEntry<'_> { struct DeriveData { resolutions: Vec, helper_attrs: Vec<(usize, IdentKey, Span)>, + // if this list keeps getting extended, we could use `bitflags`, + // something like what [`rustc_type_ir::flags::TypeFlags`] is doing. has_derive_copy: bool, + has_derive_ord: bool, } pub struct ResolverOutputs<'tcx> { @@ -1467,6 +1470,7 @@ pub struct Resolver<'ra, 'tcx> { /// Derive macros cannot modify the item themselves and have to store the markers in the global /// context, so they attach the markers to derive container IDs using this resolver table. containers_deriving_copy: FxHashSet = default::fx_hash_set(), + containers_deriving_ord: FxHashSet = default::fx_hash_set(), /// Parent scopes in which the macros were invoked. /// FIXME: `derives` are missing in these parent scopes and need to be taken from elsewhere. invocation_parent_scopes: FxHashMap> = default::fx_hash_map(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 378826c60f571..2da26e05863fd 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -379,6 +379,10 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.containers_deriving_copy.contains(&expn_id) } + fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool { + self.containers_deriving_ord.contains(&expn_id) + } + fn resolve_derives( &mut self, expn_id: LocalExpnId, @@ -398,6 +402,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { resolutions: derive_paths(), helper_attrs: Vec::new(), has_derive_copy: false, + has_derive_ord: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; for (i, resolution) in entry.resolutions.iter_mut().enumerate() { @@ -420,6 +425,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { ); } entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy); + entry.has_derive_ord |= ext.builtin_name == Some(sym::Ord); ext } Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive), @@ -455,6 +461,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) { self.containers_deriving_copy.insert(expn_id); } + // Similar to the above `Copy` and `Clone` case, the code generated for + // `derive(PartialOrd)` changes if `derive(Ord)` is also present. + // FIXME(makai410): this also doesn't work with `#[derive(PartialOrd)] #[derive(Ord)]`. + if entry.has_derive_ord || self.has_derive_ord(parent_scope.expansion) { + self.containers_deriving_ord.insert(expn_id); + } assert!(self.derive_data.is_empty()); self.derive_data = derive_data; Ok(()) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2040cd48ec44f..e82f67eac5e9f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2787,7 +2787,6 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O let first = match unstable_opts.unpretty.as_deref()? { "normal" => Source(PpSourceMode::Normal), - "identified" => Source(PpSourceMode::Identified), "expanded" => Source(PpSourceMode::Expanded), "expanded,identified" => Source(PpSourceMode::ExpandedIdentified), "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene), @@ -2803,7 +2802,7 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O "stable-mir" => StableMir, "mir-cfg" => MirCFG, name => early_dcx.early_fatal(format!( - "argument to `unpretty` must be one of `normal`, `identified`, \ + "argument to `unpretty` must be one of `normal`, \ `expanded`, `expanded,identified`, `expanded,hygiene`, \ `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \ @@ -2933,8 +2932,6 @@ pub enum PpSourceMode { Normal, /// `-Zunpretty=expanded` Expanded, - /// `-Zunpretty=identified` - Identified, /// `-Zunpretty=expanded,identified` ExpandedIdentified, /// `-Zunpretty=expanded,hygiene` @@ -2982,7 +2979,7 @@ impl PpMode { use PpMode::*; use PpSourceMode::*; match *self { - Source(Normal | Identified) | AstTree => false, + Source(Normal) | AstTree => false, Source(Expanded | ExpandedIdentified | ExpandedHygiene) | AstTreeExpanded diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aa9331ee8f659..1b2e24a684bc9 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2723,7 +2723,7 @@ written to standard error output)"), "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), unpretty: Option = (None, parse_unpretty, [UNTRACKED], "present the input source, unstable (and less-pretty) variants; - `normal`, `identified`, + `normal`, `expanded`, `expanded,identified`, `expanded,hygiene` (with internal representations), `ast-tree` (raw AST before expansion), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b7f573d3b4466..1b2eb87a21238 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -318,7 +318,6 @@ symbols! { Target, This, TokenStream, - Trait, TrivialClone, Try, TryCaptureGeneric, @@ -754,7 +753,6 @@ symbols! { custom_mir, custom_test_frameworks, d32, - dbg_macro, dead_code, dead_code_pub_in_binary, dealloc, @@ -1339,6 +1337,7 @@ symbols! { must_use, mut_preserve_binding_mode_2024, mut_ref, + mut_restriction, mutable, naked, naked_asm, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs index 13d3b77588a0e..297bd0abc864e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs @@ -30,6 +30,7 @@ pub(crate) fn target() -> Target { stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, default_uwtable: true, + supports_xray: true, ..Default::default() }; Target { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index 05876891ebd7d..f6527d236d10c 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -27,6 +27,7 @@ pub(crate) fn target() -> Target { stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, default_uwtable: true, + supports_xray: true, ..Default::default() }; Target { diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index e2bf1c48b7b47..b009c42eb2302 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -35,7 +35,12 @@ pub enum Stability { /// set in the target spec. It is never set in `cfg(target_feature)`. Used in /// particular for features are actually ABI configuration flags (not all targets are as nice as /// RISC-V and have an explicit way to set the ABI separate from target features). - Forbidden { reason: &'static str }, + Forbidden { + reason: &'static str, + /// True if this is always an error, false if this can be reported as a warning when set via + /// `-Ctarget-feature`. + hard_error: bool, + }, } use Stability::*; @@ -85,14 +90,14 @@ impl Stability { } /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. - /// (It might still be nightly-only even if this returns `true`, so make sure to also check + /// (It might still be nightly-only even if this returns `Ok(())`, so make sure to also check /// `requires_nightly`.) pub fn toggle_allowed(&self) -> Result<(), &'static str> { match self { Stability::Unstable(_) | Stability::CfgStableToggleUnstable(_) | Stability::Stable { .. } => Ok(()), - Stability::Forbidden { reason } => Err(reason), + Stability::Forbidden { reason, hard_error: _ } => Err(reason), } } } @@ -149,7 +154,10 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( "atomics-32", - Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + Stability::Forbidden { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, &[], ), ("crc", Unstable(sym::arm_target_feature), &[]), @@ -225,7 +233,11 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // FEAT_FLAGM2 ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. - ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]), + ( + "fp-armv8", + Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, + &[], + ), // FEAT_FP8 ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), // FEAT_FP8DOT2 @@ -288,7 +300,11 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM ("rdm", Stable, &["neon"]), - ("reserve-x18", Forbidden { reason: "use `-Zfixed-x18` compiler flag instead" }, &[]), + ( + "reserve-x18", + Forbidden { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, + &[], + ), // FEAT_SB ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 @@ -467,17 +483,26 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdseed", Stable, &[]), ( "retpoline-external-thunk", - Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" }, + Stability::Forbidden { + reason: "use `-Zretpoline-external-thunk` compiler flag instead", + hard_error: false, + }, &[], ), ( "retpoline-indirect-branches", - Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, + Stability::Forbidden { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, &[], ), ( "retpoline-indirect-calls", - Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, + Stability::Forbidden { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, &[], ), ("rtm", Unstable(sym::rtm_target_feature), &[]), @@ -485,7 +510,11 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sha512", Stable, &["avx2"]), ("sm3", Stable, &["avx"]), ("sm4", Stable, &["avx2"]), - ("soft-float", Stability::Forbidden { reason: "use a soft-float target instead" }, &[]), + ( + "soft-float", + Stability::Forbidden { reason: "use a soft-float target instead", hard_error: false }, + &[], + ), ("sse", Stable, &[]), ("sse2", Stable, &["sse"]), ("sse3", Stable, &["sse2"]), @@ -615,7 +644,10 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f", Unstable(sym::riscv_target_feature), &["zicsr"]), ( "forced-atomics", - Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + Stability::Forbidden { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, &[], ), ("m", Stable, &[]), @@ -872,7 +904,7 @@ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("miscellaneous-extensions-3", Stable, &[]), ("miscellaneous-extensions-4", Stable, &[]), ("nnp-assist", Stable, &["vector"]), - ("soft-float", Forbidden { reason: "unsupported ABI-configuration feature" }, &[]), + ("soft-float", Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), ("vector", Stable, &[]), ("vector-enhancements-1", Stable, &["vector"]), @@ -924,7 +956,11 @@ static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rmw", Unstable(sym::avr_target_feature), &[]), ("spm", Unstable(sym::avr_target_feature), &[]), ("spmx", Unstable(sym::avr_target_feature), &[]), - ("sram", Forbidden { reason: "devices that have no SRAM are unsupported" }, &[]), + ( + "sram", + Forbidden { reason: "devices that have no SRAM are unsupported", hard_error: false }, + &[], + ), ("tinyencoding", Unstable(sym::avr_target_feature), &[]), // tidy-alphabetical-end ]; 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 51a4186658c62..4d051a370c065 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 @@ -827,6 +827,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Keep more precise spans that still point within the parent obligation, // but do not let hidden impl details move the span outside of it. if code == *root_obligation.cause.code() + && root_obligation.cause.span.eq_ctxt(obligation.cause.span) && !root_obligation.cause.span.contains(obligation.cause.span) { obligation.cause.span = root_obligation.cause.span; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index b15f528b7261e..89dd0cf22f612 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -217,7 +217,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { })); let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); - let this_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); + let this_resolved = trait_pred.trait_ref.print_trait_sugared().to_string(); + let this_path = + ty::TraitRef::identity(self.tcx, def_id).print_only_trait_path().to_string(); let filter_options = FilterOptions { self_types, from_desugaring, cause, crate_local, direct, generic_args }; @@ -249,7 +251,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) .collect(); - let format_args = FormatArgs { this, this_sugared, generic_args, item_context }; + let format_args = FormatArgs { this, this_path, this_resolved, generic_args, item_context }; (filter_options, format_args) } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index f2ea2e5a729e8..fb61d3a467316 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -145,7 +145,7 @@ impl AsRef<[T]> for SmallCopyList { /// | never | no | no | /// | always | yes | yes | /// | [defid in storage] | no | only if any of the defids in the list is in the opaque type storage OR if TypingMode::PostAnalysis | -/// | opaque with hidden type | no | only if any of the the opaques in the opaque type storage has a hidden type in this list AND if TypingMode::Analysis | +/// | opaque with hidden type | no | only if any of the opaques in the opaque type storage has a hidden type in this list AND if TypingMode::Analysis | /// /// - "bail" is implemented with [`should_bail`](Self::should_bail). /// If true, we're abandoning our attempt to canonicalize in [`TypingMode::ErasedNotCoherence`], diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 80ce7a699efd2..bd3f10a16dd9b 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1142,7 +1142,9 @@ impl Box<[T], A> { /// /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + /// # Errors + /// + /// Returns the original `Box<[T]>` in the `Err` variant if `self.len()` does not equal `N`. /// /// # Examples /// @@ -1155,16 +1157,16 @@ impl Box<[T], A> { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[inline] #[must_use] - pub fn into_array(self) -> Option> { + pub fn into_array(self) -> Result, Self> { if self.len() == N { let (ptr, alloc) = Self::into_raw_with_allocator(self); let ptr = ptr as *mut [T; N]; // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. let me = unsafe { Box::from_raw_in(ptr, alloc) }; - Some(me) + Ok(me) } else { - None + Err(self) } } } diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index a43853604a2d2..da4b803c64d56 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -5,6 +5,7 @@ use core::ptr::NonNull; use core::{fmt, ptr}; use super::VecDeque; +use super::index::WrappedIndex; use crate::alloc::{Allocator, Global}; /// A draining iterator over the elements of a `VecDeque`. @@ -203,11 +204,11 @@ impl Drop for Drain<'_, T, A> { let (src, dst, len); if head_len < tail_len { src = source_deque.head; - dst = source_deque.to_physical_idx(drain_len); + dst = source_deque.to_wrapped_index(drain_len); len = head_len; } else { - src = source_deque.to_physical_idx(head_len + drain_len); - dst = source_deque.to_physical_idx(head_len); + src = source_deque.to_wrapped_index(head_len + drain_len); + dst = source_deque.to_wrapped_index(head_len); len = tail_len; }; @@ -220,10 +221,10 @@ impl Drop for Drain<'_, T, A> { if new_len == 0 { // Special case: If the entire deque was drained, reset the head back to 0, // like `.clear()` does. - source_deque.head = 0; + source_deque.head = WrappedIndex::zero(); } else if head_len < tail_len { // If we moved the head above, then we need to adjust the head index here. - source_deque.head = source_deque.to_physical_idx(drain_len); + source_deque.head = source_deque.to_wrapped_index(drain_len); } source_deque.len = new_len; } @@ -240,7 +241,7 @@ impl Iterator for Drain<'_, T, A> { if self.remaining == 0 { return None; } - let wrapped_idx = unsafe { self.deque.as_ref().to_physical_idx(self.idx) }; + let wrapped_idx = unsafe { self.deque.as_ref().to_wrapped_index(self.idx) }; self.idx += 1; self.remaining -= 1; Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) }) @@ -261,7 +262,8 @@ impl DoubleEndedIterator for Drain<'_, T, A> { return None; } self.remaining -= 1; - let wrapped_idx = unsafe { self.deque.as_ref().to_physical_idx(self.idx + self.remaining) }; + let wrapped_idx = + unsafe { self.deque.as_ref().to_wrapped_index(self.idx + self.remaining) }; Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) }) } } diff --git a/library/alloc/src/collections/vec_deque/extract_if.rs b/library/alloc/src/collections/vec_deque/extract_if.rs index 437f0d6dd5eb3..19439dfb4f05d 100644 --- a/library/alloc/src/collections/vec_deque/extract_if.rs +++ b/library/alloc/src/collections/vec_deque/extract_if.rs @@ -83,8 +83,8 @@ where // // Note: we can't use `vec.get_mut(i).unwrap()` here since the precondition for that // function is that i < vec.len, but we've set vec's length to zero. - let idx = self.vec.to_physical_idx(i); - let cur = unsafe { &mut *self.vec.ptr().add(idx) }; + let idx = self.vec.to_wrapped_index(i); + let cur = unsafe { &mut *self.vec.ptr().add(idx.as_index()) }; let drained = (self.pred)(cur); // Update the index *after* the predicate is called. If the index // is updated prior and the predicate panics, the element at this @@ -95,7 +95,7 @@ where // SAFETY: We never touch this element again after returning it. return Some(unsafe { ptr::read(cur) }); } else if self.del > 0 { - let hole_slot = self.vec.to_physical_idx(i - self.del); + let hole_slot = self.vec.to_wrapped_index(i - self.del); // SAFETY: `self.del` > 0, so the hole slot must not overlap with current element. // We use copy for move, and never touch this element again. unsafe { self.vec.wrap_copy(idx, hole_slot, 1) }; @@ -113,8 +113,8 @@ where impl Drop for ExtractIf<'_, T, F, A> { fn drop(&mut self) { if self.del > 0 { - let src = self.vec.to_physical_idx(self.idx); - let dst = self.vec.to_physical_idx(self.idx - self.del); + let src = self.vec.to_wrapped_index(self.idx); + let dst = self.vec.to_wrapped_index(self.idx - self.del); let len = self.old_len - self.idx; // SAFETY: Trailing unchecked items must be valid since we never touch them. unsafe { self.vec.wrap_copy(src, dst, len) }; @@ -131,7 +131,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let peek = if self.idx < self.end { - let idx = self.vec.to_physical_idx(self.idx); + let idx = self.vec.to_wrapped_index(self.idx); // This has to use pointer arithmetic as `self.vec[self.idx]` or // `self.vec.get_unchecked(self.idx)` wouldn't work since we // temporarily set the length of `self.vec` to zero. @@ -141,7 +141,7 @@ where // smaller than `self.old_len`, `idx` is valid for indexing the // buffer. Also, per the invariant of `self.idx`, this element // has not been inspected/moved out yet. - Some(unsafe { &*self.vec.ptr().add(idx) }) + Some(unsafe { &*self.vec.ptr().add(idx.as_index()) }) } else { None }; diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index 2b09a5e7ddc58..e18b85dd4b694 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -5,6 +5,7 @@ use core::ops::Try; use core::{array, fmt, ptr}; use super::VecDeque; +use super::index::WrappedIndex; use crate::alloc::{Allocator, Global}; /// An owning iterator over the elements of a `VecDeque`. @@ -86,7 +87,7 @@ impl Iterator for IntoIter { impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { fn drop(&mut self) { self.deque.len -= self.consumed; - self.deque.head = self.deque.to_physical_idx(self.consumed); + self.deque.head = self.deque.to_wrapped_index(self.consumed); } } @@ -140,7 +141,7 @@ impl Iterator for IntoIter { // SAFETY: By manually adjusting the head and length of the deque, we effectively // make it forget the first `N` elements, so taking ownership of them is safe. unsafe { ptr::copy_nonoverlapping(head.as_ptr(), raw_arr_ptr, N) }; - self.inner.head = self.inner.to_physical_idx(N); + self.inner.head = self.inner.to_wrapped_index(N); self.inner.len -= N; // SAFETY: We initialized the entire array with items from `head` return Ok(unsafe { raw_arr.transpose().assume_init() }); @@ -155,7 +156,7 @@ impl Iterator for IntoIter { unsafe { ptr::copy_nonoverlapping(tail.as_ptr(), raw_arr_ptr.add(head.len()), remaining) }; - self.inner.head = self.inner.to_physical_idx(N); + self.inner.head = self.inner.to_wrapped_index(N); self.inner.len -= N; // SAFETY: We initialized the entire array with items from `head` and `tail` Ok(unsafe { raw_arr.transpose().assume_init() }) @@ -166,7 +167,7 @@ impl Iterator for IntoIter { }; let init = head.len() + tail.len(); // We completely drained all the deques elements. - self.inner.head = 0; + self.inner.head = WrappedIndex::zero(); self.inner.len = 0; // SAFETY: We copied all elements from both slices to the beginning of the array, so // the given range is initialized. diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 43a6df7ddf753..d91b35c1f6077 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -107,7 +107,7 @@ pub struct VecDeque< > { // `self[0]`, if it exists, is `buf[head]`. // `head < buf.capacity()`, unless `buf.capacity() == 0` when `head == 0`. - head: usize, + head: WrappedIndex, // the number of initialized elements, starting from the one at `head` and potentially wrapping around. // if `len == 0`, the exact value of `head` is unimportant. // if `T` is zero-Sized, then `self.len <= usize::MAX`, otherwise `self.len <= isize::MAX as usize`. @@ -183,7 +183,7 @@ impl VecDeque { unsafe fn push_unchecked(&mut self, element: T) { // SAFETY: Because of the precondition, it's guaranteed that there is space // in the logical array after the last element. - unsafe { self.buffer_write(self.to_physical_idx(self.len), element) }; + unsafe { self.buffer_write(self.to_wrapped_index(self.len), element) }; // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`. self.len += 1; } @@ -205,8 +205,8 @@ impl VecDeque { /// Moves an element out of the buffer #[inline] - unsafe fn buffer_read(&mut self, off: usize) -> T { - unsafe { ptr::read(self.ptr().add(off)) } + unsafe fn buffer_read(&mut self, off: WrappedIndex) -> T { + unsafe { ptr::read(self.ptr().add(off.as_index())) } } /// Writes an element into the buffer, moving it and returning a pointer to it. @@ -214,9 +214,9 @@ impl VecDeque { /// /// May only be called if `off < self.capacity()`. #[inline] - unsafe fn buffer_write(&mut self, off: usize, value: T) -> &mut T { + unsafe fn buffer_write(&mut self, off: WrappedIndex, value: T) -> &mut T { unsafe { - let ptr = self.ptr().add(off); + let ptr = self.ptr().add(off.as_index()); ptr::write(ptr, value); &mut *ptr } @@ -240,20 +240,23 @@ impl VecDeque { /// Returns the index in the underlying buffer for a given logical element /// index + addend. #[inline] - fn wrap_add(&self, idx: usize, addend: usize) -> usize { - wrap_index(idx.wrapping_add(addend), self.capacity()) + fn wrap_add(&self, idx: WrappedIndex, addend: usize) -> WrappedIndex { + wrap_index(idx.as_index().wrapping_add(addend), self.capacity()) } #[inline] - fn to_physical_idx(&self, idx: usize) -> usize { + fn to_wrapped_index(&self, idx: usize) -> WrappedIndex { self.wrap_add(self.head, idx) } /// Returns the index in the underlying buffer for a given logical element /// index - subtrahend. #[inline] - fn wrap_sub(&self, idx: usize, subtrahend: usize) -> usize { - wrap_index(idx.wrapping_sub(subtrahend).wrapping_add(self.capacity()), self.capacity()) + fn wrap_sub(&self, idx: WrappedIndex, subtrahend: usize) -> WrappedIndex { + wrap_index( + idx.as_index().wrapping_sub(subtrahend).wrapping_add(self.capacity()), + self.capacity(), + ) } /// Get source, destination and count (like the arguments to [`ptr::copy_nonoverlapping`]) @@ -273,7 +276,7 @@ impl VecDeque { src: usize, dst: usize, count: usize, - head: usize, + head: WrappedIndex, ) -> [(*const T, *mut T, usize); 2] { // "`src` and `dst` must be at least as far apart as `count`" debug_assert!( @@ -289,8 +292,8 @@ impl VecDeque { let wrapped_src = self.wrap_add(head, src); let wrapped_dst = self.wrap_add(head, dst); - let room_after_src = self.capacity() - wrapped_src; - let room_after_dst = self.capacity() - wrapped_dst; + let room_after_src = self.capacity() - wrapped_src.as_index(); + let room_after_dst = self.capacity() - wrapped_dst.as_index(); let src_wraps = room_after_src < count; let dst_wraps = room_after_dst < count; @@ -305,8 +308,8 @@ impl VecDeque { unsafe { let ptr = self.ptr(); - let src_ptr = ptr.add(wrapped_src); - let dst_ptr = ptr.add(wrapped_dst); + let src_ptr = ptr.add(wrapped_src.as_index()); + let dst_ptr = ptr.add(wrapped_dst.as_index()); if src_wraps { [ @@ -330,7 +333,7 @@ impl VecDeque { /// Copies a contiguous block of memory len long from src to dst #[inline] - unsafe fn copy(&mut self, src: usize, dst: usize, len: usize) { + unsafe fn copy(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( dst + len <= self.capacity(), "cpy dst={} src={} len={} cap={}", @@ -348,13 +351,13 @@ impl VecDeque { self.capacity() ); unsafe { - ptr::copy(self.ptr().add(src), self.ptr().add(dst), len); + ptr::copy(self.ptr().add(src.as_index()), self.ptr().add(dst.as_index()), len); } } /// Copies a contiguous block of memory len long from src to dst #[inline] - unsafe fn copy_nonoverlapping(&mut self, src: usize, dst: usize, len: usize) { + unsafe fn copy_nonoverlapping(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( dst + len <= self.capacity(), "cno dst={} src={} len={} cap={}", @@ -372,14 +375,18 @@ impl VecDeque { self.capacity() ); unsafe { - ptr::copy_nonoverlapping(self.ptr().add(src), self.ptr().add(dst), len); + ptr::copy_nonoverlapping( + self.ptr().add(src.as_index()), + self.ptr().add(dst.as_index()), + len, + ); } } /// Copies a potentially wrapping block of memory len long from src to dest. /// (abs(dst - src) + len) must be no larger than capacity() (There must be at /// most one continuous overlapping region between src and dest). - unsafe fn wrap_copy(&mut self, src: usize, dst: usize, len: usize) { + unsafe fn wrap_copy(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( cmp::min(src.abs_diff(dst), self.capacity() - src.abs_diff(dst)) + len <= self.capacity(), @@ -395,10 +402,10 @@ impl VecDeque { return; } - let dst_after_src = self.wrap_sub(dst, src) < len; + let dst_after_src = self.wrap_sub(dst, src.as_index()) < len; - let src_pre_wrap_len = self.capacity() - src; - let dst_pre_wrap_len = self.capacity() - dst; + let src_pre_wrap_len = self.capacity() - src.as_index(); + let dst_pre_wrap_len = self.capacity() - dst.as_index(); let src_wraps = src_pre_wrap_len < len; let dst_wraps = dst_pre_wrap_len < len; @@ -426,7 +433,11 @@ impl VecDeque { // unsafe { self.copy(src, dst, dst_pre_wrap_len); - self.copy(src + dst_pre_wrap_len, 0, len - dst_pre_wrap_len); + self.copy( + src.add(dst_pre_wrap_len), + WrappedIndex::zero(), + len - dst_pre_wrap_len, + ); } } (true, false, true) => { @@ -439,7 +450,11 @@ impl VecDeque { // . . D . // unsafe { - self.copy(src + dst_pre_wrap_len, 0, len - dst_pre_wrap_len); + self.copy( + src.add(dst_pre_wrap_len), + WrappedIndex::zero(), + len - dst_pre_wrap_len, + ); self.copy(src, dst, dst_pre_wrap_len); } } @@ -454,7 +469,11 @@ impl VecDeque { // unsafe { self.copy(src, dst, src_pre_wrap_len); - self.copy(0, dst + src_pre_wrap_len, len - src_pre_wrap_len); + self.copy( + WrappedIndex::zero(), + dst.add(src_pre_wrap_len), + len - src_pre_wrap_len, + ); } } (true, true, false) => { @@ -467,7 +486,11 @@ impl VecDeque { // D . . . // unsafe { - self.copy(0, dst + src_pre_wrap_len, len - src_pre_wrap_len); + self.copy( + WrappedIndex::zero(), + dst.add(src_pre_wrap_len), + len - src_pre_wrap_len, + ); self.copy(src, dst, src_pre_wrap_len); } } @@ -485,8 +508,12 @@ impl VecDeque { let delta = dst_pre_wrap_len - src_pre_wrap_len; unsafe { self.copy(src, dst, src_pre_wrap_len); - self.copy(0, dst + src_pre_wrap_len, delta); - self.copy(delta, 0, len - dst_pre_wrap_len); + self.copy(WrappedIndex::zero(), dst.add(src_pre_wrap_len), delta); + self.copy( + WrappedIndex::from_arbitrary_number(delta), + WrappedIndex::zero(), + len - dst_pre_wrap_len, + ); } } (true, true, true) => { @@ -502,8 +529,16 @@ impl VecDeque { debug_assert!(src_pre_wrap_len > dst_pre_wrap_len); let delta = src_pre_wrap_len - dst_pre_wrap_len; unsafe { - self.copy(0, delta, len - src_pre_wrap_len); - self.copy(self.capacity() - delta, 0, delta); + self.copy( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(delta), + len - src_pre_wrap_len, + ); + self.copy( + WrappedIndex::from_arbitrary_number(self.capacity() - delta), + WrappedIndex::zero(), + delta, + ); self.copy(src, dst, dst_pre_wrap_len); } } @@ -513,17 +548,17 @@ impl VecDeque { /// Copies all values from `src` to `dst`, wrapping around if needed. /// Assumes capacity is sufficient. #[inline] - unsafe fn copy_slice(&mut self, dst: usize, src: &[T]) { + unsafe fn copy_slice(&mut self, dst: WrappedIndex, src: &[T]) { debug_assert!(src.len() <= self.capacity()); - let head_room = self.capacity() - dst; + let head_room = self.capacity() - dst.as_index(); if src.len() <= head_room { unsafe { - ptr::copy_nonoverlapping(src.as_ptr(), self.ptr().add(dst), src.len()); + ptr::copy_nonoverlapping(src.as_ptr(), self.ptr().add(dst.as_index()), src.len()); } } else { let (left, right) = src.split_at(head_room); unsafe { - ptr::copy_nonoverlapping(left.as_ptr(), self.ptr().add(dst), left.len()); + ptr::copy_nonoverlapping(left.as_ptr(), self.ptr().add(dst.as_index()), left.len()); ptr::copy_nonoverlapping(right.as_ptr(), self.ptr(), right.len()); } } @@ -533,7 +568,7 @@ impl VecDeque { /// Assumes capacity is sufficient. /// Equivalent to calling [`VecDeque::copy_slice`] with a [reversed](https://doc.rust-lang.org/std/primitive.slice.html#method.reverse) slice. #[inline] - unsafe fn copy_slice_reversed(&mut self, dst: usize, src: &[T]) { + unsafe fn copy_slice_reversed(&mut self, dst: WrappedIndex, src: &[T]) { /// # Safety /// /// See [`ptr::copy_nonoverlapping`]. @@ -544,15 +579,23 @@ impl VecDeque { } debug_assert!(src.len() <= self.capacity()); - let head_room = self.capacity() - dst; + let head_room = self.capacity() - dst.as_index(); if src.len() <= head_room { unsafe { - copy_nonoverlapping_reversed(src.as_ptr(), self.ptr().add(dst), src.len()); + copy_nonoverlapping_reversed( + src.as_ptr(), + self.ptr().add(dst.as_index()), + src.len(), + ); } } else { let (left, right) = src.split_at(src.len() - head_room); unsafe { - copy_nonoverlapping_reversed(right.as_ptr(), self.ptr().add(dst), right.len()); + copy_nonoverlapping_reversed( + right.as_ptr(), + self.ptr().add(dst.as_index()), + right.len(), + ); copy_nonoverlapping_reversed(left.as_ptr(), self.ptr(), left.len()); } } @@ -567,12 +610,12 @@ impl VecDeque { #[inline] unsafe fn write_iter( &mut self, - dst: usize, + dst: WrappedIndex, iter: impl Iterator, written: &mut usize, ) { iter.enumerate().for_each(|(i, element)| unsafe { - self.buffer_write(dst + i, element); + self.buffer_write(dst.add(i), element); *written += 1; }); } @@ -587,7 +630,7 @@ impl VecDeque { /// Assumes capacity is sufficient. unsafe fn write_iter_wrapping( &mut self, - dst: usize, + dst: WrappedIndex, mut iter: impl Iterator, len: usize, ) -> usize { @@ -602,7 +645,7 @@ impl VecDeque { } } - let head_room = self.capacity() - dst; + let head_room = self.capacity() - dst.as_index(); let mut guard = Guard { deque: self, written: 0 }; @@ -615,7 +658,7 @@ impl VecDeque { ByRefSized(&mut iter).take(head_room), &mut guard.written, ); - guard.deque.write_iter(0, iter, &mut guard.written) + guard.deque.write_iter(WrappedIndex::zero(), iter, &mut guard.written) }; } @@ -652,16 +695,20 @@ impl VecDeque { // A // Nop } else { - let head_len = old_capacity - self.head; + let head_len = old_capacity - self.head.as_index(); let tail_len = self.len - head_len; if head_len > tail_len && new_capacity - old_capacity >= tail_len { // B unsafe { - self.copy_nonoverlapping(0, old_capacity, tail_len); + self.copy_nonoverlapping( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(old_capacity), + tail_len, + ); } } else { // C - let new_head = new_capacity - head_len; + let new_head = WrappedIndex::from_arbitrary_number(new_capacity - head_len); unsafe { // can't use copy_nonoverlapping here, because if e.g. head_len = 2 // and new_capacity = old_capacity + 1, then the heads overlap. @@ -780,7 +827,7 @@ impl VecDeque { #[must_use] pub const fn new() -> VecDeque { // FIXME(const-hack): This should just be `VecDeque::new_in(Global)` once that hits stable. - VecDeque { head: 0, len: 0, buf: RawVec::new() } + VecDeque { head: WrappedIndex::zero(), len: 0, buf: RawVec::new() } } /// Creates an empty deque with space for at least `capacity` elements. @@ -820,7 +867,11 @@ impl VecDeque { #[inline] #[unstable(feature = "try_with_capacity", issue = "91913")] pub fn try_with_capacity(capacity: usize) -> Result, TryReserveError> { - Ok(VecDeque { head: 0, len: 0, buf: RawVec::try_with_capacity_in(capacity, Global)? }) + Ok(VecDeque { + head: WrappedIndex::zero(), + len: 0, + buf: RawVec::try_with_capacity_in(capacity, Global)?, + }) } } @@ -840,7 +891,7 @@ impl VecDeque { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub const fn new_in(alloc: A) -> VecDeque { - VecDeque { head: 0, len: 0, buf: RawVec::new_in(alloc) } + VecDeque { head: WrappedIndex::zero(), len: 0, buf: RawVec::new_in(alloc) } } /// Creates an empty deque with space for at least `capacity` elements. @@ -857,7 +908,11 @@ impl VecDeque { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque { - VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) } + VecDeque { + head: WrappedIndex::zero(), + len: 0, + buf: RawVec::with_capacity_in(capacity, alloc), + } } /// Creates a `VecDeque` from a raw allocation, when the initialized @@ -886,7 +941,7 @@ impl VecDeque { // and that the allocation is valid for use in `RawVec`. unsafe { VecDeque { - head: initialized.start, + head: WrappedIndex::from_arbitrary_number(initialized.start), len: initialized.end.unchecked_sub(initialized.start), buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), } @@ -912,8 +967,8 @@ impl VecDeque { #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self, index: usize) -> Option<&T> { if index < self.len { - let idx = self.to_physical_idx(index); - unsafe { Some(&*self.ptr().add(idx)) } + let idx = self.to_wrapped_index(index); + unsafe { Some(&*self.ptr().add(idx.as_index())) } } else { None } @@ -942,8 +997,8 @@ impl VecDeque { #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index < self.len { - let idx = self.to_physical_idx(index); - unsafe { Some(&mut *self.ptr().add(idx)) } + let idx = self.to_wrapped_index(index); + unsafe { Some(&mut *self.ptr().add(idx.as_index())) } } else { None } @@ -976,9 +1031,9 @@ impl VecDeque { pub fn swap(&mut self, i: usize, j: usize) { assert!(i < self.len()); assert!(j < self.len()); - let ri = self.to_physical_idx(i); - let rj = self.to_physical_idx(j); - unsafe { ptr::swap(self.ptr().add(ri), self.ptr().add(rj)) } + let ri = self.to_wrapped_index(i); + let rj = self.to_wrapped_index(j); + unsafe { ptr::swap(self.ptr().add(ri.as_index()), self.ptr().add(rj.as_index())) } } /// Returns the number of elements the deque can hold without @@ -1229,8 +1284,8 @@ impl VecDeque { let old_head = self.head; if self.len == 0 { - self.head = 0; - } else if self.head >= target_cap && tail_outside { + self.head = WrappedIndex::zero(); + } else if self.head.as_index() >= target_cap && tail_outside { // Head and tail are both out of bounds, so copy all of them to the front. // // H := head @@ -1241,9 +1296,9 @@ impl VecDeque { // [o o o o o o o . ] unsafe { // nonoverlapping because `self.head >= target_cap >= self.len`. - self.copy_nonoverlapping(self.head, 0, self.len); + self.copy_nonoverlapping(self.head, WrappedIndex::zero(), self.len); } - self.head = 0; + self.head = WrappedIndex::zero(); } else if self.head < target_cap && tail_outside { // Head is in bounds, tail is out of bounds. // Copy the overflowing part to the beginning of the @@ -1256,8 +1311,13 @@ impl VecDeque { // L H // [o o . o o o o o ] let len = self.head + self.len - target_cap; + // Safety: head is < target_cap, so the index is wrapped unsafe { - self.copy_nonoverlapping(target_cap, 0, len); + self.copy_nonoverlapping( + WrappedIndex::from_arbitrary_number(target_cap), + WrappedIndex::zero(), + len, + ); } } else if !self.is_contiguous() { // The head slice is at least partially out of bounds, tail is in bounds. @@ -1270,8 +1330,10 @@ impl VecDeque { // [o o o o o . . . . . . . . . o o ] // L H // [o o o o o . o o ] - let head_len = self.capacity() - self.head; - let new_head = target_cap - head_len; + let head_len = self.capacity() - self.head.as_index(); + + // head_len is at least one, so new_head will be < target_cap + let new_head = WrappedIndex::from_arbitrary_number(target_cap - head_len); unsafe { // can't use `copy_nonoverlapping()` here because the new and old // regions for the head might overlap. @@ -1282,7 +1344,7 @@ impl VecDeque { struct Guard<'a, T, A: Allocator> { deque: &'a mut VecDeque, - old_head: usize, + old_head: WrappedIndex, target_cap: usize, } @@ -1314,7 +1376,7 @@ impl VecDeque { /// /// `old_head` refers to the head index before `shrink_to` was called. `target_cap` /// is the capacity that it was trying to shrink to. - unsafe fn abort_shrink(&mut self, old_head: usize, target_cap: usize) { + unsafe fn abort_shrink(&mut self, old_head: WrappedIndex, target_cap: usize) { // Moral equivalent of self.head + self.len <= target_cap. Won't overflow // because `self.len <= target_cap`. if self.head <= target_cap - self.len { @@ -1323,7 +1385,7 @@ impl VecDeque { } // `shrink_to` already copied the head to fit into the new capacity, so this won't overflow. - let head_len = target_cap - self.head; + let head_len = target_cap - self.head.as_index(); // `self.head > target_cap - self.len` => `self.len > target_cap - self.head =: head_len` so this must be positive. let tail_len = self.len - head_len; @@ -1334,7 +1396,11 @@ impl VecDeque { unsafe { // The old tail and the new tail can't overlap because the head slice lies between them. The // head slice ends at `target_cap`, so that's where we copy to. - self.copy_nonoverlapping(0, target_cap, tail_len); + self.copy_nonoverlapping( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(target_cap), + tail_len, + ); } } else { // Either there's not enough spare capacity to make the deque contiguous, or the head is shorter than the tail @@ -1459,7 +1525,7 @@ impl VecDeque { // and end < front.len() let end = front.len() - (len - back.len()); let drop_front = front.get_unchecked_mut(..end) as *mut _; - self.head += end; + self.head = self.head.add(end); self.len = len; ptr::drop_in_place(drop_front); } else { @@ -1467,7 +1533,7 @@ impl VecDeque { // 'end' is non-negative by the condition above let end = back.len() - len; let drop_back = back.get_unchecked_mut(..end) as *mut _; - self.head = self.to_physical_idx(self.len - len); + self.head = self.to_wrapped_index(self.len - len); self.len = len; // Make sure the second half is dropped even when a destructor @@ -1682,20 +1748,20 @@ impl VecDeque { // `slice::range` guarantees that `start <= end <= len`. // because `len != 0`, we know that `start < end`, so `start < len` // and the indexing is valid. - let wrapped_start = self.to_physical_idx(start); + let wrapped_start = self.to_wrapped_index(start); // this subtraction can never overflow because `wrapped_start` is // at most `self.capacity()` (and if `self.capacity != 0`, then `wrapped_start` is strictly less // than `self.capacity`). - let head_len = self.capacity() - wrapped_start; + let head_len = self.capacity() - wrapped_start.as_index(); if head_len >= len { // we know that `len + wrapped_start <= self.capacity <= usize::MAX`, so this addition can't overflow - (wrapped_start..wrapped_start + len, 0..0) + (wrapped_start.as_index()..wrapped_start + len, 0..0) } else { // can't overflow because of the if condition let tail_len = len - head_len; - (wrapped_start..self.capacity(), 0..tail_len) + (wrapped_start.as_index()..self.capacity(), 0..tail_len) } } } @@ -1926,7 +1992,7 @@ impl VecDeque { pub fn clear(&mut self) { self.truncate(0); // Not strictly necessary, but leaves things in a more consistent/predictable state. - self.head = 0; + self.head = WrappedIndex::zero(); } /// Returns `true` if the deque contains an element equal to the @@ -2072,7 +2138,7 @@ impl VecDeque { None } else { let old_head = self.head; - self.head = self.to_physical_idx(1); + self.head = self.to_wrapped_index(1); self.len -= 1; unsafe { core::hint::assert_unchecked(self.len < self.capacity()); @@ -2103,7 +2169,7 @@ impl VecDeque { self.len -= 1; unsafe { core::hint::assert_unchecked(self.len < self.capacity()); - Some(self.buffer_read(self.to_physical_idx(self.len))) + Some(self.buffer_read(self.to_wrapped_index(self.len))) } } } @@ -2233,7 +2299,7 @@ impl VecDeque { let len = self.len; self.len += 1; - unsafe { self.buffer_write(self.to_physical_idx(len), value) } + unsafe { self.buffer_write(self.to_wrapped_index(len), value) } } /// Prepends all contents of the iterator to the front of the deque. @@ -2447,9 +2513,9 @@ impl VecDeque { // and panicked. unsafe { // see `remove()` for explanation why this wrap_copy() call is safe. - self.wrap_copy(self.to_physical_idx(index), self.to_physical_idx(index + 1), k); + self.wrap_copy(self.to_wrapped_index(index), self.to_wrapped_index(index + 1), k); self.len += 1; - self.buffer_write(self.to_physical_idx(index), value) + self.buffer_write(self.to_wrapped_index(index), value) } } else { let old_head = self.head; @@ -2457,7 +2523,7 @@ impl VecDeque { unsafe { self.wrap_copy(old_head, self.head, index); self.len += 1; - self.buffer_write(self.to_physical_idx(index), value) + self.buffer_write(self.to_wrapped_index(index), value) } } } @@ -2490,7 +2556,7 @@ impl VecDeque { return None; } - let wrapped_idx = self.to_physical_idx(index); + let wrapped_idx = self.to_wrapped_index(index); let elem = unsafe { Some(self.buffer_read(wrapped_idx)) }; @@ -2503,7 +2569,7 @@ impl VecDeque { self.len -= 1; } else { let old_head = self.head; - self.head = self.to_physical_idx(1); + self.head = self.to_wrapped_index(1); unsafe { self.wrap_copy(old_head, self.head, index) }; self.len -= 1; } @@ -2607,23 +2673,23 @@ impl VecDeque { if T::IS_ZST { self.len = self.len.checked_add(other.len).expect("capacity overflow"); other.len = 0; - other.head = 0; + other.head = WrappedIndex::zero(); return; } self.reserve(other.len); unsafe { let (left, right) = other.as_slices(); - self.copy_slice(self.to_physical_idx(self.len), left); + self.copy_slice(self.to_wrapped_index(self.len), left); // no overflow, because self.capacity() >= old_cap + left.len() >= self.len + left.len() - self.copy_slice(self.to_physical_idx(self.len + left.len()), right); + self.copy_slice(self.to_wrapped_index(self.len + left.len()), right); } // SAFETY: Update pointers after copying to avoid leaving doppelganger // in case of panics. self.len += other.len; // Now that we own its values, forget everything in `other`. other.len = 0; - other.head = 0; + other.head = WrappedIndex::zero(); } /// Retains only the elements specified by the predicate. @@ -2831,11 +2897,13 @@ impl VecDeque { #[stable(feature = "deque_make_contiguous", since = "1.48.0")] pub fn make_contiguous(&mut self) -> &mut [T] { if T::IS_ZST { - self.head = 0; + self.head = WrappedIndex::zero(); } if self.is_contiguous() { - unsafe { return slice::from_raw_parts_mut(self.ptr().add(self.head), self.len) } + unsafe { + return slice::from_raw_parts_mut(self.ptr().add(self.head.as_index()), self.len); + } } let &mut Self { head, len, .. } = self; @@ -2843,9 +2911,13 @@ impl VecDeque { let cap = self.capacity(); let free = cap - len; - let head_len = cap - head; - let tail = len - head_len; - let tail_len = tail; + let head_len = cap - head.as_index(); + + // tail <= head < capacity + // head cannot be <= capacity, because we know that VecDeque is non-empty, since it is not + // contiguous at this point + let tail = WrappedIndex::from_arbitrary_number(len - head_len); + let tail_len = tail.as_index(); if free >= head_len { // there is enough free space to copy the head in one go, @@ -2855,13 +2927,17 @@ impl VecDeque { // from: DEFGH....ABC // to: ABCDEFGH.... unsafe { - self.copy(0, head_len, tail_len); + self.copy( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(head_len), + tail_len, + ); // ...DEFGH.ABC - self.copy_nonoverlapping(head, 0, head_len); + self.copy_nonoverlapping(head, WrappedIndex::zero(), head_len); // ABCDEFGH.... } - self.head = 0; + self.head = WrappedIndex::zero(); } else if free >= tail_len { // there is enough free space to copy the tail in one go, // this means that we first shift the head forwards, and then @@ -2872,7 +2948,7 @@ impl VecDeque { unsafe { self.copy(head, tail, head_len); // FGHABCDE.... - self.copy_nonoverlapping(0, tail + head_len, tail_len); + self.copy_nonoverlapping(WrappedIndex::zero(), tail.add(head_len), tail_len); // ...ABCDEFGH. } @@ -2908,7 +2984,11 @@ impl VecDeque { // because we only move the tail forward as much as there's free space // behind it, we don't overwrite any elements of the head slice, and // the slices end up right next to each other. - self.copy(0, free, tail_len); + self.copy( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(free), + tail_len, + ); } // We just copied the tail right next to the head slice, @@ -2921,7 +3001,7 @@ impl VecDeque { // the used part of the buffer now is `free..self.capacity()`, so set // `head` to the beginning of that range. - self.head = free; + self.head = WrappedIndex::from_arbitrary_number(free); } } else { // head is shorter so: @@ -2934,7 +3014,11 @@ impl VecDeque { // right next to each other and we don't need to move any memory. if free != 0 { // copy the head slice to lie right behind the tail slice. - self.copy(self.head, tail_len, head_len); + self.copy( + self.head, + WrappedIndex::from_arbitrary_number(tail_len), + head_len, + ); } // because we copied the head slice so that both slices lie right @@ -2947,12 +3031,12 @@ impl VecDeque { // the used part of the buffer now is `0..self.len`, so set // `head` to the beginning of that range. - self.head = 0; + self.head = WrappedIndex::zero(); } } } - unsafe { slice::from_raw_parts_mut(ptr.add(self.head), self.len) } + unsafe { slice::from_raw_parts_mut(ptr.add(self.head.as_index()), self.len) } } /// Rotates the double-ended queue `n` places to the left. @@ -3052,16 +3136,16 @@ impl VecDeque { unsafe fn rotate_left_inner(&mut self, mid: usize) { debug_assert!(mid * 2 <= self.len()); unsafe { - self.wrap_copy(self.head, self.to_physical_idx(self.len), mid); + self.wrap_copy(self.head, self.to_wrapped_index(self.len), mid); } - self.head = self.to_physical_idx(mid); + self.head = self.to_wrapped_index(mid); } unsafe fn rotate_right_inner(&mut self, k: usize) { debug_assert!(k * 2 <= self.len()); self.head = self.wrap_sub(self.head, k); unsafe { - self.wrap_copy(self.to_physical_idx(self.len), self.head, k); + self.wrap_copy(self.to_wrapped_index(self.len), self.head, k); } } @@ -3462,7 +3546,7 @@ impl SpecExtendFromWithin for VecDeque { let (src, dst, count) = ranges[1]; for offset in (0..count).rev() { dst.add(offset).write((*src.add(offset)).clone()); - self.head -= 1; + self.head = self.head.sub(1); self.len += 1; } @@ -3472,16 +3556,18 @@ impl SpecExtendFromWithin for VecDeque { if let Some(offset) = iter.next() { dst.add(offset).write((*src.add(offset)).clone()); // After the first clone of the second range, wrap `head` around - if self.head == 0 { - self.head = cap; + if self.head.is_zero() { + // SAFETY: the wrapped index may be temporarily equal to the capacity even if it + // is not zero, because we subtract it one line below. + self.head = WrappedIndex::from_arbitrary_number(cap); } - self.head -= 1; + self.head = self.head.sub(1); self.len += 1; // Continue like normal for offset in iter { dst.add(offset).write((*src.add(offset)).clone()); - self.head -= 1; + self.head = self.head.sub(1); self.len += 1; } } @@ -3535,15 +3621,109 @@ impl SpecExtendFromWithin for VecDeque { } } -/// Returns the index in the underlying buffer for a given logical element index. -#[inline] -fn wrap_index(logical_index: usize, capacity: usize) -> usize { - debug_assert!( - (logical_index == 0 && capacity == 0) - || logical_index < capacity - || (logical_index - capacity) < capacity - ); - if logical_index >= capacity { logical_index - capacity } else { logical_index } +use index::{WrappedIndex, wrap_index}; + +// The code is separated into a module to make it harder to construct a BufferIndex without +// going through wrapping. +mod index { + use core::cmp::Ordering; + + /// Returns the index in the underlying buffer for a given logical element index. + #[inline] + pub(super) fn wrap_index(logical_index: usize, capacity: usize) -> WrappedIndex { + debug_assert!( + (logical_index == 0 && capacity == 0) + || logical_index < capacity + || (logical_index - capacity) < capacity + ); + if logical_index >= capacity { + WrappedIndex(logical_index - capacity) + } else { + WrappedIndex(logical_index) + } + } + + /// Represents an index that can be safely used to index the VecDeque buffer. + /// It exists as a separate type to avoid passing logical (unwrapped) indices to various + /// VecDeque functions by accident. + /// + /// The invariant of this index is that it is always < VecDeque capacity, unless the VecDeque + /// is empty (in that case the index can be 0 when the capacity is 0). + #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] + #[repr(transparent)] + pub(super) struct WrappedIndex(usize); + + impl WrappedIndex { + /// The newly constructed index has to be in-bounds for the VecDeque + /// that uses the index. + #[inline(always)] + pub(super) fn from_arbitrary_number(index: usize) -> Self { + Self(index) + } + + /// Safety invariant: the newly constructed index must still be in-bounds for the VecDeque + #[inline(always)] + pub(super) unsafe fn add(self, offset: usize) -> Self { + Self(self.0 + offset) + } + + /// Safety invariant: the newly constructed index must still be in-bounds for the VecDeque + #[inline(always)] + pub(super) unsafe fn sub(self, offset: usize) -> Self { + debug_assert!(self.0 >= offset); + Self(self.0 - offset) + } + + #[inline(always)] + pub(super) const fn zero() -> Self { + Self(0) + } + + #[inline(always)] + pub(super) fn abs_diff(self, other: Self) -> usize { + self.0.abs_diff(other.0) + } + + #[inline(always)] + pub(super) fn as_index(self) -> usize { + self.0 + } + + #[inline(always)] + pub(super) fn is_zero(self) -> bool { + self.0 == 0 + } + } + + impl core::ops::Add for WrappedIndex { + // The output might not be wrapped anymore + type Output = usize; + + #[inline(always)] + fn add(self, rhs: usize) -> Self::Output { + self.0 + rhs + } + } + + impl core::fmt::Display for WrappedIndex { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } + } + + impl core::cmp::PartialEq for WrappedIndex { + #[inline(always)] + fn eq(&self, other: &usize) -> bool { + self.0.eq(other) + } + } + + impl core::cmp::PartialOrd for WrappedIndex { + #[inline(always)] + fn partial_cmp(&self, other: &usize) -> Option { + self.0.partial_cmp(other) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -3751,7 +3931,11 @@ impl From> for VecDeque { #[inline] fn from(other: Vec) -> Self { let (ptr, len, cap, alloc) = other.into_raw_parts_with_alloc(); - Self { head: 0, len, buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) } } + Self { + head: WrappedIndex::zero(), + len, + buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) }, + } } } @@ -3796,8 +3980,8 @@ impl From> for Vec { let cap = other.capacity(); let alloc = ptr::read(other.allocator()); - if other.head != 0 { - ptr::copy(buf.add(other.head), buf, len); + if !other.head.is_zero() { + ptr::copy(buf.add(other.head.as_index()), buf, len); } Vec::from_raw_parts_in(buf, len, cap, alloc) } @@ -3824,7 +4008,7 @@ impl From<[T; N]> for VecDeque { ptr::copy_nonoverlapping(arr.as_ptr(), deq.ptr(), N); } } - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = N; deq } diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index ae48d0ea46789..0699d403d9de5 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -58,7 +58,7 @@ where self.reserve(additional); let written = unsafe { - self.write_iter_wrapping(self.to_physical_idx(self.len), iter, additional) + self.write_iter_wrapping(self.to_wrapped_index(self.len), iter, additional) }; debug_assert_eq!( @@ -83,7 +83,7 @@ impl SpecExtend> for Ve self.reserve(slice.len()); unsafe { - self.copy_slice(self.to_physical_idx(self.len), slice); + self.copy_slice(self.to_wrapped_index(self.len), slice); self.len += slice.len(); } iterator.forget_remaining_elements_and_dealloc(); @@ -109,7 +109,7 @@ where self.reserve(slice.len()); unsafe { - self.copy_slice(self.to_physical_idx(self.len), slice); + self.copy_slice(self.to_wrapped_index(self.len), slice); self.len += slice.len(); } } diff --git a/library/alloc/src/collections/vec_deque/splice.rs b/library/alloc/src/collections/vec_deque/splice.rs index b82f9fba7ceb3..a29e9c3742564 100644 --- a/library/alloc/src/collections/vec_deque/splice.rs +++ b/library/alloc/src/collections/vec_deque/splice.rs @@ -120,7 +120,7 @@ impl Drain<'_, T, A> { for idx in range_start..range_end { if let Some(new_item) = replace_with.next() { - let index = deque.to_physical_idx(idx); + let index = deque.to_wrapped_index(idx); unsafe { deque.buffer_write(index, new_item) }; deque.len += 1; self.drain_len -= 1; @@ -184,8 +184,8 @@ impl Drain<'_, T, A> { let new_tail_start = tail_start + additional; unsafe { deque.wrap_copy( - deque.to_physical_idx(tail_start), - deque.to_physical_idx(new_tail_start), + deque.to_wrapped_index(tail_start), + deque.to_wrapped_index(new_tail_start), self.tail_len, ); } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index dc50cc34d9dac..d9cf56de7bb6b 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -12,7 +12,7 @@ fn bench_push_back_100(b: &mut test::Bencher) { for i in 0..100 { deq.push_back(i); } - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 0; }) } @@ -24,7 +24,7 @@ fn bench_push_front_100(b: &mut test::Bencher) { for i in 0..100 { deq.push_front(i); } - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 0; }) } @@ -38,7 +38,7 @@ fn bench_pop_back_100(b: &mut test::Bencher) { unsafe { deq.ptr().write_bytes(0u8, size + 1) }; b.iter(|| { - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 100; while !deq.is_empty() { test::black_box(deq.pop_back()); @@ -88,7 +88,7 @@ fn bench_pop_front_100(b: &mut test::Bencher) { unsafe { deq.ptr().write_bytes(0u8, size + 1) }; b.iter(|| { - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 100; while !deq.is_empty() { test::black_box(deq.pop_front()); @@ -109,7 +109,7 @@ fn test_swap_front_back_remove() { let expected: VecDeque<_> = if back { (0..len).collect() } else { (0..len).rev().collect() }; for head_pos in 0..usable_cap { - tester.head = head_pos; + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; if back { for i in 0..len * 2 { @@ -127,7 +127,7 @@ fn test_swap_front_back_remove() { assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i)); } } - assert!(tester.head <= tester.capacity()); + assert!(tester.head.as_index() <= tester.capacity()); assert!(tester.len <= tester.capacity()); assert_eq!(tester, expected); } @@ -155,7 +155,7 @@ fn test_insert() { let expected = (0..).take(len).collect::>(); for head_pos in 0..cap { for to_insert in 0..len { - tester.head = head_pos; + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; for i in 0..len { if i != to_insert { @@ -636,7 +636,7 @@ fn test_remove() { let expected = (0..).take(len).collect::>(); for head_pos in 0..cap { for to_remove in 0..=len { - tester.head = head_pos; + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; for i in 0..len { if i == to_remove { @@ -666,7 +666,7 @@ fn test_range() { for head in 0..=cap { for start in 0..=len { for end in start..=len { - tester.head = head; + tester.head = WrappedIndex::from_arbitrary_number(head); tester.len = 0; for i in 0..len { tester.push_back(i); @@ -691,7 +691,7 @@ fn test_range_mut() { for head in 0..=cap { for start in 0..=len { for end in start..=len { - tester.head = head; + tester.head = WrappedIndex::from_arbitrary_number(head); tester.len = 0; for i in 0..len { tester.push_back(i); @@ -725,7 +725,7 @@ fn test_drain() { for head in 0..cap { for drain_start in 0..=len { for drain_end in drain_start..=len { - tester.head = head; + tester.head = WrappedIndex::from_arbitrary_number(head); tester.len = 0; for i in 0..len { tester.push_back(i); @@ -782,7 +782,7 @@ fn test_shrink_to() { assert_eq!(deque.capacity(), cap); // we can let the head point anywhere in the buffer since the deque is empty. - deque.head = head; + deque.head = WrappedIndex::from_arbitrary_number(head); deque.extend(1..=len); deque.shrink_to(target_cap); @@ -811,7 +811,7 @@ fn test_shrink_to_fit() { let expected = (0..).take(len).collect::>(); for head_pos in 0..=max_cap { tester.reserve(head_pos); - tester.head = head_pos; + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; tester.reserve(63); for i in 0..len { @@ -848,7 +848,7 @@ fn test_split_off() { let expected_other = (at..).take(len - at).collect::>(); for head_pos in 0..cap { - tester.head = head_pos; + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; for i in 0..len { tester.push_back(i); diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7b41023ff31bf..5fe5464ab2cdd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -144,6 +144,7 @@ #![feature(ptr_metadata)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] +#![feature(share_trait)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 2905170d22a76..2491489517688 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -245,7 +245,7 @@ use core::any::Any; use core::cell::{Cell, CloneFromCell}; #[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -use core::clone::{CloneToUninit, UseCloned}; +use core::clone::{CloneToUninit, Share, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -1245,7 +1245,9 @@ impl Rc<[T], A> { /// /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + /// # Errors + /// + /// Returns the original `Rc<[T]>` in the `Err` variant if `self.len()` does not equal `N`. /// /// # Examples /// @@ -1260,16 +1262,16 @@ impl Rc<[T], A> { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[inline] #[must_use] - pub fn into_array(self) -> Option> { + pub fn into_array(self) -> Result, Self> { if self.len() == N { let (ptr, alloc) = Self::into_raw_with_allocator(self); let ptr = ptr as *const [T; N]; // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. let me = unsafe { Rc::from_raw_in(ptr, alloc) }; - Some(me) + Ok(me) } else { - None + Err(self) } } } @@ -2525,6 +2527,9 @@ impl Clone for Rc { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl UseCloned for Rc {} +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Rc {} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Rc { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 229fcd2b429cf..597d26d3239ad 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -12,7 +12,7 @@ use core::any::Any; use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -use core::clone::{CloneToUninit, UseCloned}; +use core::clone::{CloneToUninit, Share, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -1392,7 +1392,9 @@ impl Arc<[T], A> { /// /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + /// # Errors + /// + /// Returns the original `Arc<[T]>` in the `Err` variant if `self.len()` does not equal `N`. /// /// # Examples /// @@ -1407,16 +1409,16 @@ impl Arc<[T], A> { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[inline] #[must_use] - pub fn into_array(self) -> Option> { + pub fn into_array(self) -> Result, Self> { if self.len() == N { let (ptr, alloc) = Self::into_raw_with_allocator(self); let ptr = ptr as *const [T; N]; // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. let me = unsafe { Arc::from_raw_in(ptr, alloc) }; - Some(me) + Ok(me) } else { - None + Err(self) } } } @@ -2436,6 +2438,9 @@ impl Clone for Arc { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl UseCloned for Arc {} +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Arc {} + #[stable(feature = "rust1", since = "1.0.0")] impl Deref for Arc { type Target = T; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 27f85666d719a..d2b7837d98fa6 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1740,10 +1740,13 @@ impl Vec { } } - /// Converts the Vec into a boxed array. This conversion will discard any spare capacity, if there is any, see [`Vec::shrink_to_fit`]. + /// Converts the Vec into a boxed array. This conversion will discard any spare capacity, + /// if there is any, see [`Vec::shrink_to_fit`]. /// If you merely wish for a reference to an array, use [`as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array). /// - /// If `N` is not exactly equal to [`Vec::len`], then this method returns `None`. + /// # Errors + /// + /// Returns the original `Vec` in the `Err` variant if [`Vec::len`] does not equal `N`. /// /// # Examples /// @@ -1758,7 +1761,11 @@ impl Vec { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[must_use] pub fn into_array(self) -> Result, Self> { - if self.len() == N { Ok(self.into_boxed_slice().into_array().unwrap()) } else { Err(self) } + if self.len() == N { + Ok(self.into_boxed_slice().into_array().ok().unwrap()) + } else { + Err(self) + } } /// Shortens the vector, keeping the first `len` elements and dropping diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 9b9d2f02550bd..751b8d0f1b762 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -78,15 +78,11 @@ impl bool { /// # Examples /// /// ``` - /// #![feature(bool_to_result)] - /// /// assert_eq!(false.ok_or(0), Err(0)); /// assert_eq!(true.ok_or(0), Ok(())); /// ``` /// /// ``` - /// #![feature(bool_to_result)] - /// /// let mut a = 0; /// let mut function_with_side_effects = || { a += 1; }; /// @@ -97,7 +93,7 @@ impl bool { /// // evaluated eagerly. /// assert_eq!(a, 2); /// ``` - #[unstable(feature = "bool_to_result", issue = "142748")] + #[stable(feature = "bool_to_result", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] pub const fn ok_or(self, err: E) -> Result<(), E> { @@ -110,15 +106,11 @@ impl bool { /// # Examples /// /// ``` - /// #![feature(bool_to_result)] - /// /// assert_eq!(false.ok_or_else(|| 0), Err(0)); /// assert_eq!(true.ok_or_else(|| 0), Ok(())); /// ``` /// /// ``` - /// #![feature(bool_to_result)] - /// /// let mut a = 0; /// /// assert!(true.ok_or_else(|| { a += 1; }).is_ok()); @@ -128,7 +120,7 @@ impl bool { /// // `ok_or_else`. /// assert_eq!(a, 1); /// ``` - #[unstable(feature = "bool_to_result", issue = "142748")] + #[stable(feature = "bool_to_result", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] pub const fn ok_or_else E + [const] Destruct>( diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index f2fa6fd0ca3e9..0c3e90b78103a 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -290,6 +290,90 @@ pub macro Clone($item:item) { /* compiler built-in */ } +/// A trait for types whose [`Clone`] operation creates another alias to the same +/// logical resource or shared state. +/// +/// `Share` marks types where cloning creates another handle, reference, or alias +/// to the same logical resource or shared state, rather than an independent owned +/// value. The distinction is semantic, not cost-based: implementing `Share` does +/// not merely mean that cloning is cheap, constant-time, allocation-free, or +/// convenient. +/// +/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) +/// for implementors, but communicates that the resulting value aliases the same +/// underlying resource. +/// +/// Shared references, `Rc`, `Arc`, `Sender`, and `SyncSender` are +/// examples of types that can be shared this way. Types such as `Vec`, +/// `String`, and `Box` are not `Share` even though they implement `Clone`, +/// because cloning them creates another owned value rather than another handle +/// to the same logical resource. +/// +/// # Examples +/// +/// ``` +/// #![feature(share_trait)] +/// +/// use std::cell::Cell; +/// use std::clone::Share; +/// use std::rc::Rc; +/// use std::sync::{ +/// Arc, +/// atomic::{AtomicUsize, Ordering}, +/// }; +/// +/// let value = 1; +/// let reference = &value; +/// assert!(std::ptr::eq(reference, reference.share())); +/// +/// let rc = Rc::new(Cell::new(2)); +/// let shared_rc = rc.share(); +/// assert!(Rc::ptr_eq(&rc, &shared_rc)); +/// shared_rc.set(3); +/// assert_eq!(rc.get(), 3); +/// +/// let arc = Arc::new(AtomicUsize::new(4)); +/// let shared_arc = arc.share(); +/// assert!(Arc::ptr_eq(&arc, &shared_arc)); +/// shared_arc.store(5, Ordering::Relaxed); +/// assert_eq!(arc.load(Ordering::Relaxed), 5); +/// ``` +/// +/// ``` +/// #![feature(share_trait)] +/// +/// use std::clone::Share; +/// use std::sync::mpsc::{channel, sync_channel}; +/// +/// let (sender, receiver) = channel(); +/// let shared_sender = sender.share(); +/// sender.send(1).unwrap(); +/// shared_sender.send(2).unwrap(); +/// +/// let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()]; +/// received.sort(); +/// assert_eq!(received, [1, 2]); +/// +/// let (sync_sender, sync_receiver) = sync_channel(2); +/// let shared_sync_sender = sync_sender.share(); +/// sync_sender.send(3).unwrap(); +/// shared_sync_sender.send(4).unwrap(); +/// +/// let mut received = [sync_receiver.recv().unwrap(), sync_receiver.recv().unwrap()]; +/// received.sort(); +/// assert_eq!(received, [3, 4]); +/// ``` +#[unstable(feature = "share_trait", issue = "156756")] +pub trait Share: Clone { + /// Creates another alias to the same underlying resource or shared state. + /// + /// This is equivalent to calling [`Clone::clone`]. + #[unstable(feature = "share_trait", issue = "156756")] + fn share(&self) -> Self { + Clone::clone(self) + } +} + /// Trait for objects whose [`Clone`] impl is lightweight (e.g. reference-counted) /// /// Cloning an object implementing this trait should in general: @@ -601,7 +685,7 @@ unsafe impl CloneToUninit for crate::bstr::ByteStr { /// are implemented in `traits::SelectionContext::copy_clone_conditions()` /// in `rustc_trait_selection`. mod impls { - use super::TrivialClone; + use super::{Share, TrivialClone}; use crate::marker::PointeeSized; macro_rules! impl_clone { @@ -689,6 +773,9 @@ mod impls { #[rustc_const_unstable(feature = "const_clone", issue = "142757")] unsafe impl const TrivialClone for &T {} + #[unstable(feature = "share_trait", issue = "156756")] + impl Share for &T {} + /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] impl !Clone for &mut T {} diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 85d42b57dc06e..46b0e8c6b67cd 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -5,11 +5,10 @@ //! //! - Implement the [`AsRef`] trait for cheap reference-to-reference conversions //! - Implement the [`AsMut`] trait for cheap mutable-to-mutable conversions -//! - Implement the [`From`] trait for consuming value-to-value conversions -//! - Implement the [`Into`] trait for consuming value-to-value conversions to types -//! outside the current crate -//! - The [`TryFrom`] and [`TryInto`] traits behave like [`From`] and [`Into`], -//! but should be implemented when the conversion can fail. +//! - Implement the [`From`] trait for consuming value-to-value conversions that cannot fail. This +//! automatically provides an implementation of [`Into`] +//! - Implement the [`TryFrom`] trait for consuming value-to-value conversions that can fail. This +//! automatically provides an implementation of [`TryInto`] //! //! The traits in this module are often used as trait bounds for generic functions such that //! arguments of multiple types are supported. See the documentation of each trait for examples. @@ -18,9 +17,9 @@ //! [`TryFrom`][`TryFrom`] rather than [`Into`][`Into`] or [`TryInto`][`TryInto`], //! as [`From`] and [`TryFrom`] provide greater flexibility and offer //! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a -//! blanket implementation in the standard library. When targeting a version prior to Rust 1.41, it -//! may be necessary to implement [`Into`] or [`TryInto`] directly when converting to a type -//! outside the current crate. +//! blanket implementation in the standard library. In versions of Rust prior to Rust 1.41, +//! it was sometimes necessary to implement [`Into`] or [`TryInto`] directly when converting to a +//! type outside the current crate. //! //! # Generic Implementations //! diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index 055c50833d94d..cc3680b8a42be 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -34,6 +34,22 @@ impl_NumBufferTrait! { /// A buffer wrapper of which the internal size is based on the maximum /// number of digits the associated integer can have. +/// +/// # Examples +/// +/// ``` +/// #![feature(int_format_into)] +/// use core::fmt::NumBuffer; +/// +/// let mut buf = NumBuffer::new(); +/// let n1 = 1972u32; +/// assert_eq!(n1.format_into(&mut buf), "1972"); +/// +/// // Formatting a negative integer includes the sign. +/// let mut buf = NumBuffer::new(); +/// let n2 = -1972i32; +/// assert_eq!(n2.format_into(&mut buf), "-1972"); +/// ``` #[unstable(feature = "int_format_into", issue = "138215")] pub struct NumBuffer { // FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3e18f87e20537..acc758a75e77b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -166,7 +166,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_inline_always)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d381402b469f4..cd1987aa959dd 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -33,6 +33,7 @@ pub mod consts { pub const TAU: f128 = 6.28318530717958647692528676655900576839433879875021164194989_f128; /// The golden ratio (φ) + #[doc(alias = "phi")] #[unstable(feature = "f128", issue = "116909")] pub const GOLDEN_RATIO: f128 = 1.61803398874989484820458683436563811772030917980576286213545_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index c26ae17d870cc..ef8eb3c0b57a0 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -35,6 +35,7 @@ pub mod consts { pub const TAU: f16 = 6.28318530717958647692528676655900577_f16; /// The golden ratio (φ) + #[doc(alias = "phi")] #[unstable(feature = "f16", issue = "116909")] pub const GOLDEN_RATIO: f16 = 1.618033988749894848204586834365638118_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 24c97a6491c11..2a7506f198589 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -292,6 +292,7 @@ pub mod consts { pub const TAU: f32 = 6.28318530717958647692528676655900577_f32; /// The golden ratio (φ) + #[doc(alias = "phi")] #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] pub const GOLDEN_RATIO: f32 = 1.618033988749894848204586834365638118_f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index be045033a3553..8b77a3b7b0bde 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -292,6 +292,7 @@ pub mod consts { pub const TAU: f64 = 6.28318530717958647692528676655900577_f64; /// The golden ratio (φ) + #[doc(alias = "phi")] #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] pub const GOLDEN_RATIO: f64 = 1.618033988749894848204586834365638118_f64; diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index efe5c0871fb8b..57b41e6ad6ccd 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -67,8 +67,8 @@ use crate::marker::Tuple; // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{Trait}` closure, found `{Self}`", - label = "expected an `{Trait}` closure, found `{Self}`" + message = "expected a `{This:resolved}` closure, found `{Self}`", + label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] @@ -154,8 +154,8 @@ pub const trait Fn: [const] FnMut { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{Trait}` closure, found `{Self}`", - label = "expected an `{Trait}` closure, found `{Self}`" + message = "expected a `{This:resolved}` closure, found `{Self}`", + label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] @@ -233,8 +233,8 @@ pub const trait FnMut: FnOnce { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{Trait}` closure, found `{Self}`", - label = "expected an `{Trait}` closure, found `{Self}`" + message = "expected a `{This:resolved}` closure, found `{Self}`", + label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 18e09c707ebad..d1e706beb51b6 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,7 +8,6 @@ #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] -#![feature(bool_to_result)] #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 047dfbe1a2333..150ce9c1b9a96 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -1072,41 +1072,42 @@ pub mod consts { /// ///
Full list of possible values /// - /// * `"linux"` - /// * `"windows"` - /// * `"macos"` - /// * `"android"` - /// * `"ios"` - /// * `"openbsd"` - /// * `"freebsd"` - /// * `"netbsd"` - /// * `"wasi"` - /// * `"hermit"` + // tidy-alphabetical-start /// * `"aix"` + /// * `"android"` /// * `"apple"` /// * `"dragonfly"` /// * `"emscripten"` /// * `"espidf"` /// * `"fortanix"` - /// * `"uefi"` + /// * `"freebsd"` /// * `"fuchsia"` /// * `"haiku"` /// * `"hermit"` - /// * `"watchos"` - /// * `"visionos"` - /// * `"tvos"` /// * `"horizon"` /// * `"hurd"` /// * `"illumos"` + /// * `"ios"` /// * `"l4re"` + /// * `"linux"` + /// * `"macos"` + /// * `"netbsd"` /// * `"nto"` + /// * `"openbsd"` /// * `"redox"` /// * `"solaris"` /// * `"solid_asp3"` + /// * `"tvos"` + /// * `"uefi"` /// * `"vexos"` + /// * `"visionos"` /// * `"vita"` /// * `"vxworks"` + /// * `"wasi"` + /// * `"watchos"` + /// * `"windows"` /// * `"xous"` + // tidy-alphabetical-end /// ///
#[stable(feature = "env", since = "1.0.0")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7a548dfb33da7..cb0f8edb7b852 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -255,7 +255,10 @@ #![allow(unused_features)] // // Features: -#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] +#![cfg_attr( + test, + feature(internal_output_capture, print_internals, super_let, update_panic_count, rt) +)] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) @@ -303,7 +306,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_inline_always)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(try_trait_v2)] @@ -367,6 +369,7 @@ #![feature(random)] #![feature(raw_os_error_ty)] #![feature(seek_io_take_position)] +#![feature(share_trait)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] @@ -488,9 +491,7 @@ extern crate std as realstd; // The standard macros that are not built-in to the compiler. #[macro_use] -#[doc(hidden)] -#[unstable(feature = "std_internals", issue = "none")] -pub mod macros; +mod macros; // The runtime entry point and a few unstable public functions used by the // compiler diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index a23aa0d877018..8bcf870e9aeb4 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -350,76 +350,37 @@ macro_rules! eprintln { /// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html /// [`log`]: https://crates.io/crates/log #[macro_export] -#[allow_internal_unstable(std_internals)] #[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[stable(feature = "dbg_macro", since = "1.32.0")] macro_rules! dbg { + // NOTE: We cannot use `concat!` to make a static string as a format argument + // of `eprintln!` because `file!` could contain a `{` or + // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` + // will be malformed. () => { $crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!()) }; - ($($val:expr),+ $(,)?) => { - $crate::macros::dbg_internal!(() () ($($val),+)) - }; -} - -/// Internal macro that processes a list of expressions, binds their results -/// with `match`, calls `eprint!` with the collected information, and returns -/// all the evaluated expressions in a tuple. -/// -/// E.g. `dbg_internal!(() () (1, 2))` expands into -/// ```rust, ignore -/// match (1, 2) { -/// args => { -/// let (tmp_1, tmp_2) = args; -/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); -/// (tmp_1, tmp_2) -/// } -/// } -/// ``` -/// -/// This is necessary so that `dbg!` outputs don't get torn, see #136703. -#[doc(hidden)] -#[rustc_macro_transparency = "semiopaque"] -pub macro dbg_internal { - (($($piece:literal),+) ($($processed:expr => $bound:ident),+) ()) => { + ($val:expr $(,)?) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 - // Always put the arguments in a tuple to avoid an unused parens lint on the pattern. - match ($($processed,)+) { - // Move the entire tuple so it doesn't stick around as a temporary (#154988). - args => { - let ($($bound,)+) = args; - $crate::eprint!( - $crate::concat!($($piece),+), - $( - $crate::stringify!($processed), - // The `&T: Debug` check happens here (not in the format literal desugaring) - // to avoid format literal related messages and suggestions. - &&$bound as &dyn $crate::fmt::Debug - ),+, - // The location returned here is that of the macro invocation, so - // it will be the same for all expressions. Thus, label these - // arguments so that they can be reused in every piece of the - // formatting template. - file=$crate::file!(), - line=$crate::line!(), - column=$crate::column!() + match $val { + tmp => { + $crate::eprintln!("[{}:{}:{}] {} = {:#?}", + $crate::file!(), + $crate::line!(), + $crate::column!(), + $crate::stringify!($val), + // The `&T: Debug` check happens here (not in the format literal desugaring) + // to avoid format literal related messages and suggestions. + &&tmp as &dyn $crate::fmt::Debug, ); - // Comma separate the variables only when necessary so that this will - // not yield a tuple for a single expression, but rather just parenthesize - // the expression. - ($($bound),+) - + tmp } } - }, - (($($piece:literal),*) ($($processed:expr => $bound:ident),*) ($val:expr $(,$rest:expr)*)) => { - $crate::macros::dbg_internal!( - ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") - ($($processed => $bound,)* $val => tmp) - ($($rest),*) - ) - }, + }; + ($($val:expr),+ $(,)?) => { + ($($crate::dbg!($val)),+,) + }; } #[doc(hidden)] diff --git a/library/std/src/macros/tests.rs b/library/std/src/macros/tests.rs index 230bfdf3c9836..1ad4cf8c4c039 100644 --- a/library/std/src/macros/tests.rs +++ b/library/std/src/macros/tests.rs @@ -35,3 +35,33 @@ fn no_leaking_internal_temps_from_dbg() { // is dropped, then `foo`, then any temporaries left over from `dbg!` are dropped, if present. (drop(dbg!(bar)), drop(foo)); } + +/// Test for : +/// `dbg!` shouldn't create a temporary that borrowck thinks can live past its invocation on a false +/// unwind path. +#[test] +fn no_leaking_internal_temps_from_dbg_even_on_false_unwind() { + #[derive(Debug)] + struct Foo; + impl Drop for Foo { + fn drop(&mut self) {} + } + + #[derive(Debug)] + struct Bar<'a>(#[allow(unused)] &'a Foo); + impl Drop for Bar<'_> { + fn drop(&mut self) {} + } + + { + let foo = Foo; + let bar = Bar(&foo); + // The temporaries of this `super let`'s scrutinee will outlive `bar` and `foo`, emulating + // the drop order of block tail expressions before Rust 2024. If borrowck thinks that a + // panic from moving `bar` is possible and that a `Bar<'_>`-containing temporary lives past + // the end of the block because of that, this will fail to compile. Because `Foo` implements + // `Drop`, it's an error for `foo` to be dropped before such a temporary when unwinding; + // otherwise, `foo` would just live to the end of the stack frame when unwinding. + super let _ = drop(dbg!(bar)); + } +} diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index f6fe8e1b2353b..8a3a7ee7c5cba 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,17 +1,14 @@ +use rand::Rng; + use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; -use crate::net::test::{next_test_ip4, next_test_ip6}; +use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6}; use crate::net::*; use crate::sync::mpsc::channel; use crate::time::{Duration, Instant}; use crate::{fmt, thread}; -fn each_ip(f: &mut dyn FnMut(SocketAddr)) { - f(next_test_ip4()); - f(next_test_ip6()); -} - macro_rules! t { ($e:expr) => { match $e { @@ -21,6 +18,11 @@ macro_rules! t { }; } +fn each_ip(f: &mut dyn FnMut(TcpListener)) { + f(t!(TcpListener::bind(LOCALHOST_IP4))); + f(t!(TcpListener::bind(LOCALHOST_IP6))); +} + #[test] fn bind_error() { match TcpListener::bind("1.1.1.1:9999") { @@ -46,10 +48,18 @@ fn connect_error() { } } +/// Regression test for `Duration::MAX` timeout issue +/// (https://github.com/rust-lang/rust/issues/112405). #[test] #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn connect_timeout_error() { - let socket_addr = next_test_ip4(); + // Pick a random port, but not in the range usually used by the OS's automatic port assignment + // so we cannot conflict with the other tests. + let mut rng = crate::test_helpers::test_rng(); + let port = rng.random_range((16 * 1024)..(48 * 1024)); + let socket_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)); + + // Ensure we error immediately rather than timing out. let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX); assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut)); @@ -60,11 +70,11 @@ fn connect_timeout_error() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn listen_localhost() { - let socket_addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&socket_addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let addr = t!(listener.local_addr()); let _t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); + let mut stream = t!(TcpStream::connect(addr)); t!(stream.write(&[144])); }); @@ -77,15 +87,11 @@ fn listen_localhost() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn connect_loopback() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { - let host = match addr { - SocketAddr::V4(..) => "127.0.0.1", - SocketAddr::V6(..) => "::1", - }; - let mut stream = t!(TcpStream::connect(&(host, addr.port()))); + let mut stream = t!(TcpStream::connect(addr)); t!(stream.write(&[66])); }); @@ -99,8 +105,8 @@ fn connect_loopback() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn smoke_test() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let (tx, rx) = channel(); let _t = thread::spawn(move || { @@ -120,8 +126,8 @@ fn smoke_test() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn read_eof() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let _stream = t!(TcpStream::connect(&addr)); @@ -140,8 +146,8 @@ fn read_eof() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn write_close() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let (tx, rx) = channel(); let _t = thread::spawn(move || { @@ -169,9 +175,9 @@ fn write_close() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn multiple_connect_serial() { - each_ip(&mut |addr| { + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let max = 10; - let acceptor = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { for _ in 0..max { @@ -193,8 +199,8 @@ fn multiple_connect_serial() { #[cfg_attr(target_os = "wasi", ignore)] // no threads fn multiple_connect_interleaved_greedy_schedule() { const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let acceptor = acceptor; @@ -231,8 +237,8 @@ fn multiple_connect_interleaved_greedy_schedule() { #[cfg_attr(target_os = "wasi", ignore)] // no threads fn multiple_connect_interleaved_lazy_schedule() { const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { for stream in acceptor.incoming().take(MAX) { @@ -266,25 +272,25 @@ fn multiple_connect_interleaved_lazy_schedule() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn socket_and_peer_name() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); - let so_name = t!(listener.local_addr()); - assert_eq!(addr, so_name); - let _t = thread::spawn(move || { - t!(listener.accept()); - }); + each_ip(&mut |listener| { + let addr = t!(listener.local_addr()); + let other_stream = thread::spawn(move || t!(listener.accept())); let stream = t!(TcpStream::connect(&addr)); assert_eq!(addr, t!(stream.peer_addr())); + let (other_stream, other_peer) = other_stream.join().unwrap(); + assert_eq!(addr, t!(other_stream.local_addr())); + assert_eq!(other_peer, t!(other_stream.peer_addr())); + assert_eq!(other_peer, t!(stream.local_addr())); }) } #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn partial_read() { - each_ip(&mut |addr| { + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let (tx, rx) = channel(); - let srv = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { let mut cl = t!(srv.accept()).0; cl.write(&[10]).unwrap(); @@ -304,8 +310,8 @@ fn partial_read() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn read_buf() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let t = thread::spawn(move || { let mut s = t!(TcpStream::connect(&addr)); s.write_all(&[1, 2, 3, 4]).unwrap(); @@ -325,8 +331,8 @@ fn read_buf() { #[test] fn read_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let mut s1 = t!(TcpStream::connect(&addr)); let mut s2 = t!(srv.accept()).0; @@ -350,8 +356,8 @@ fn read_vectored() { #[test] fn write_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let mut s1 = t!(TcpStream::connect(&addr)); let mut s2 = t!(srv.accept()).0; @@ -374,8 +380,8 @@ fn write_vectored() { #[test] fn double_bind() { - each_ip(&mut |addr| { - let listener1 = t!(TcpListener::bind(&addr)); + each_ip(&mut |listener1| { + let addr = t!(listener1.local_addr()); match TcpListener::bind(&addr) { Ok(listener2) => panic!( "This system (perhaps due to options set by TcpListener::bind) \ @@ -399,8 +405,8 @@ fn double_bind() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn tcp_clone_smoke() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let mut s = t!(TcpStream::connect(&addr)); @@ -431,8 +437,8 @@ fn tcp_clone_smoke() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn tcp_clone_two_read() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let (tx1, rx) = channel(); let tx2 = tx1.clone(); @@ -466,8 +472,8 @@ fn tcp_clone_two_read() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn tcp_clone_two_write() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let mut s = t!(TcpStream::connect(&addr)); @@ -496,8 +502,8 @@ fn tcp_clone_two_write() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn shutdown_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let _t = thread::spawn(move || { let mut c = t!(a.accept()).0; let mut b = [0]; @@ -519,8 +525,8 @@ fn shutdown_smoke() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn close_readwrite_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let (tx, rx) = channel::<()>(); let _t = thread::spawn(move || { let _s = t!(a.accept()); @@ -562,8 +568,8 @@ fn close_readwrite_smoke() { #[cfg_attr(windows, ignore)] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn close_read_wakes_up() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); + each_ip(&mut |listener| { + let addr = t!(listener.local_addr()); let _t = thread::spawn(move || { let (stream, _) = t!(listener.accept()); stream @@ -590,8 +596,8 @@ fn close_read_wakes_up() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn clone_while_reading() { - each_ip(&mut |addr| { - let accept = t!(TcpListener::bind(&addr)); + each_ip(&mut |accept| { + let addr = t!(accept.local_addr()); // Enqueue a thread to write to a socket let (tx, rx) = channel(); @@ -631,8 +637,8 @@ fn clone_while_reading() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn clone_accept_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let a2 = t!(a.try_clone()); let _t = thread::spawn(move || { @@ -650,8 +656,8 @@ fn clone_accept_smoke() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn clone_accept_concurrent() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let a2 = t!(a.try_clone()); let (tx, rx) = channel(); @@ -701,9 +707,9 @@ fn debug() { } let inner_name = if cfg!(windows) { "socket" } else { "fd" }; - let socket_addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&socket_addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let socket_addr = t!(listener.local_addr()); let compare = format!( "TcpListener {{ addr: {:?}, {}: {:?} }}", render_socket_addr(&socket_addr), @@ -734,10 +740,9 @@ fn debug() { #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported #[test] fn timeouts() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); let dur = Duration::new(15410, 0); assert_eq!(None, t!(stream.read_timeout())); @@ -762,10 +767,9 @@ fn timeouts() { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported fn test_read_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); let mut buf = [0; 10]; @@ -784,10 +788,9 @@ fn test_read_timeout() { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported fn test_read_with_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); let mut other_end = t!(listener.accept()).0; @@ -812,10 +815,8 @@ fn test_read_with_timeout() { // when passed zero Durations #[test] fn test_timeout_zero_duration() { - let addr = next_test_ip4(); - - let listener = t!(TcpListener::bind(&addr)); - let stream = t!(TcpStream::connect(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); let result = stream.set_write_timeout(Some(Duration::new(0, 0))); let err = result.unwrap_err(); @@ -832,10 +833,9 @@ fn test_timeout_zero_duration() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] // linger not supported fn linger() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); assert_eq!(None, t!(stream.linger())); t!(stream.set_linger(Some(Duration::from_secs(1)))); @@ -848,9 +848,8 @@ fn linger() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] fn keepalive() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); - let stream = t!(TcpStream::connect(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); assert_eq!(false, t!(stream.keepalive())); t!(stream.set_keepalive(true)); @@ -862,10 +861,9 @@ fn keepalive() { #[test] #[cfg_attr(target_env = "sgx", ignore)] fn nodelay() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); assert_eq!(false, t!(stream.nodelay())); t!(stream.set_nodelay(true)); @@ -879,13 +877,12 @@ fn nodelay() { fn ttl() { let ttl = 100; - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); t!(listener.set_ttl(ttl)); assert_eq!(ttl, t!(listener.ttl())); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_ttl(ttl)); assert_eq!(ttl, t!(stream.ttl())); @@ -894,13 +891,12 @@ fn ttl() { #[test] #[cfg_attr(target_env = "sgx", ignore)] fn set_nonblocking() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); t!(listener.set_nonblocking(true)); t!(listener.set_nonblocking(false)); - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_nonblocking(false)); t!(stream.set_nonblocking(true)); @@ -917,10 +913,11 @@ fn set_nonblocking() { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[cfg_attr(target_os = "wasi", ignore)] // no threads fn peek() { - each_ip(&mut |addr| { + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); + let (txdone, rxdone) = channel(); - let srv = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { let mut cl = t!(srv.accept()).0; cl.write(&[1, 3, 3, 7]).unwrap(); diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index df48b2f2420c3..68cbf7b669232 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -7,6 +7,13 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); const BASE_PORT: u16 = 19600; +/// A localhost address whose port will be picked automatically by the OS. +pub const LOCALHOST_IP4: SocketAddr = + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0)); +/// A localhost address whose port will be picked automatically by the OS. +pub const LOCALHOST_IP6: SocketAddr = + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 0, 0, 0)); + pub fn next_test_ip4() -> SocketAddr { let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index 8c40f07f0d17d..b74f84ff465c0 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -142,6 +142,8 @@ // not exposed publicly, but if you are curious about the implementation, // that's where everything is. +use core::clone::Share; + use crate::sync::mpmc; use crate::time::{Duration, Instant}; use crate::{error, fmt}; @@ -645,6 +647,9 @@ impl Clone for Sender { } } +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Sender {} + #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -774,6 +779,9 @@ impl Clone for SyncSender { } } +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for SyncSender {} + #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for SyncSender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 5c84e856d54bb..98f6b842d135f 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -281,10 +281,19 @@ jobs: - armv7-unknown-linux-gnueabihf - x86_64-unknown-linux-gnu profile: [dev, release] + cc: [clang, gcc] include: - target: aarch64_be-unknown-linux-gnu build_std: true - + - target: x86_64-unknown-linux-gnu + cc: icx + profile: dev + - target: x86_64-unknown-linux-gnu + cc: icx + profile: release + exclude: + - target: armv7-unknown-linux-gnueabihf + cc: gcc steps: - uses: actions/checkout@v6 - name: Install Rust @@ -297,10 +306,11 @@ jobs: rustup component add rust-src echo "CARGO_UNSTABLE_BUILD_STD=std" >> $GITHUB_ENV if: ${{ matrix.build_std }} + - run: rustup component add rustfmt # Configure some env vars based on matrix configuration - run: echo "PROFILE=${{ matrix.profile }}" >> $GITHUB_ENV - - run: ./ci/intrinsic-test-docker.sh ${{ matrix.target }} + - run: ./ci/intrinsic-test-docker.sh ${{ matrix.target }} ${{ matrix.cc }} if: ${{ !startsWith(matrix.target, 'thumb') }} env: TARGET: ${{ matrix.target }} @@ -308,7 +318,7 @@ jobs: # Check that the generated files agree with the checked-in versions. check-stdarch-gen: needs: [style] - name: Check stdarch-gen-{arm, loongarch, hexagon} output + name: Check stdarch-gen-{arm, loongarch, hexagon, hexagon-scalar} output runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -330,6 +340,10 @@ jobs: run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code + - name: Check hexagon scalar + run: | + cargo run -p stdarch-gen-hexagon-scalar --release + git diff --exit-code # Run some tests with Miri. Most stdarch functions use platform-specific intrinsics # that Miri does not support. Also Miri is reltively slow. diff --git a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index e2b3d95585efe..1b61dd0c1b87a 100644 --- a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -15,7 +15,8 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH=aarch64-linux-gnu-gcc ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -cpu max -L /usr/aarch64-linux-gnu" \ diff --git a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile index d7c12493ad9cf..70acc2d22a418 100644 --- a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile @@ -23,11 +23,12 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" - ENV AARCH64_BE_TOOLCHAIN="/toolchains/${TOOLCHAIN}" ENV AARCH64_BE_LIBC="${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-gcc" + ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_LINKER="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-gcc" ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64_be -cpu max -L ${AARCH64_BE_LIBC}" ENV OBJDUMP="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-objdump" diff --git a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 02744917af6df..3c8a1b5add27f 100644 --- a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -14,7 +14,8 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH=arm-linux-gnueabihf-gcc ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -cpu max -L /usr/arm-linux-gnueabihf" \ diff --git a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index 17d1ac67e714f..efbb2b0853371 100644 --- a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -6,7 +6,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ make \ ca-certificates \ wget \ - xz-utils + xz-utils \ + gpg RUN wget http://ci-mirrors.rust-lang.org/sde-external-10.8.0-2026-03-15-lin.tar.xz -O sde.tar.xz RUN mkdir intel-sde @@ -16,7 +17,18 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" +RUN wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB |\ + gpg --dearmor |\ + tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null + +RUN echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" |\ + tee /etc/apt/sources.list.d/oneAPI.list + +RUN apt-get update && apt-get install -y --no-install-recommends intel-oneapi-compiler-dpcpp-cpp + +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH="gcc" +ENV ICX_PATH="/opt/intel/oneapi/compiler/2026.0/bin/icx" ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="/intel-sde/sde64 \ -cpuid-in /checkout/ci/docker/x86_64-unknown-linux-gnu/cpuid.def \ diff --git a/library/stdarch/ci/intrinsic-test-docker.sh b/library/stdarch/ci/intrinsic-test-docker.sh index 948b53dc67bc9..c1d44dca91c5e 100755 --- a/library/stdarch/ci/intrinsic-test-docker.sh +++ b/library/stdarch/ci/intrinsic-test-docker.sh @@ -5,8 +5,8 @@ set -ex -if [ $# -lt 1 ]; then - >&2 echo "Usage: $0 " +if [ $# -lt 2 ]; then + >&2 echo "Usage: $0 " exit 1 fi @@ -29,7 +29,6 @@ run() { --user "$(id -u)":"$(id -g)" \ --env CARGO_HOME=/cargo \ --env CARGO_TARGET_DIR=/checkout/target \ - --env TARGET="${1}" \ --env PROFILE \ --env "${HOST_LINKER}"="cc" \ --env STDARCH_DISABLE_ASSERT_INSTR \ @@ -48,12 +47,12 @@ run() { --workdir /checkout \ --privileged \ stdarch \ - sh -c "HOME=/tmp PATH=\$PATH:/rust/bin exec ci/intrinsic-test.sh" + sh -c "HOME=/tmp PATH=\$PATH:/rust/bin exec ci/intrinsic-test.sh ${1} ${2}" } if [ -z "$1" ]; then >&2 echo "No target specified!" exit 1 else - run "${1}" + run "${1}" "${2}" fi diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 1f3a2caf50654..0441611f38fd2 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -2,7 +2,30 @@ set -ex -: "${TARGET?The TARGET environment variable must be set.}" +if [ $# -lt 2 ]; then + >&2 echo "Usage: $0 " + exit 1 +fi + +case ${2} in + clang) + export CC="${CLANG_PATH}" + CC_ARG_STYLE=clang + ;; + gcc) + export CC="${GCC_PATH}" + CC_ARG_STYLE=gcc + ;; + icx) + export CC="${ICX_PATH}" + # `icx` uses clang-style arguments + CC_ARG_STYLE=clang + ;; + *) + >&2 echo "Unknown compiler: ${2}" + exit 1 + ;; +esac export RUSTFLAGS="${RUSTFLAGS} -D warnings -Z merge-functions=disabled -Z verify-llvm-ir" export PROFILE="${PROFILE:="release"}" @@ -12,49 +35,49 @@ echo "PROFILE=${PROFILE}" INTRINSIC_TEST="--manifest-path=crates/intrinsic-test/Cargo.toml" -export CC="clang" - -case ${TARGET} in +case ${1} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_aarch64_be.txt + ARCH=aarch64_be ;; aarch64*) export CFLAGS="-I/usr/aarch64-linux-gnu/include/" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_aarch64.txt + ARCH=aarch64 ;; armv7*) export CFLAGS="-I/usr/arm-linux-gnueabihf/include/" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_arm.txt + ARCH=arm ;; x86_64*) export CFLAGS="-I/usr/include/x86_64-linux-gnu/" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_x86.txt + ARCH=x86 ;; *) ;; esac -case "${TARGET}" in +case "${1}" in x86_64-unknown-linux-gnu*) env -u CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER \ cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/x86-intel.xml \ - --skip "${TEST_SKIP_INTRINSICS}" \ - --target "${TARGET}" - - echo "${CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER}" + --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ + --target "${1}" \ + --cc-arg-style "${CC_ARG_STYLE}" ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ - --skip "${TEST_SKIP_INTRINSICS}" \ - --target "${TARGET}" + --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ + --target "${1}" \ + --cc-arg-style "${CC_ARG_STYLE}" ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" +cargo test --manifest-path=rust_programs/Cargo.toml --target "${1}" --profile "${PROFILE}" --tests diff --git a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs index c906ec5166a19..477414de74b85 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs @@ -11425,7 +11425,7 @@ pub unsafe fn Q6_l2fetch_AP(rs: i32, rtt: i64) { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11440,7 +11440,7 @@ pub unsafe fn Q6_P_rol_PI(rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11455,7 +11455,7 @@ pub unsafe fn Q6_P_rolacc_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11470,7 +11470,7 @@ pub unsafe fn Q6_P_roland_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11485,7 +11485,7 @@ pub unsafe fn Q6_P_rolnac_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11500,7 +11500,7 @@ pub unsafe fn Q6_P_rolor_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11515,7 +11515,7 @@ pub unsafe fn Q6_P_rolxacc_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11530,7 +11530,7 @@ pub unsafe fn Q6_R_rol_RI(rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11545,7 +11545,7 @@ pub unsafe fn Q6_R_rolacc_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11560,7 +11560,7 @@ pub unsafe fn Q6_R_roland_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11575,7 +11575,7 @@ pub unsafe fn Q6_R_rolnac_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11590,7 +11590,7 @@ pub unsafe fn Q6_R_rolor_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11605,7 +11605,7 @@ pub unsafe fn Q6_R_rolxacc_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vabsdiffb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11618,7 +11618,7 @@ pub unsafe fn Q6_P_vabsdiffb_PP(rtt: i64, rss: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vabsdiffub))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11631,7 +11631,7 @@ pub unsafe fn Q6_P_vabsdiffub_PP(rtt: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vsplatb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11644,7 +11644,7 @@ pub unsafe fn Q6_P_vsplatb_R(rs: i32) -> i64 { /// Instruction Type: S_3op /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vtrunehb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11657,7 +11657,7 @@ pub unsafe fn Q6_P_vtrunehb_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: S_3op /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vtrunohb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11670,7 +11670,7 @@ pub unsafe fn Q6_P_vtrunohb_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: ALU64 /// Execution Slots: SLOT23 /// Requires: V65 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v65"))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] pub unsafe fn Q6_p_not_any8_vcmpb_eq_PP(rss: i64, rtt: i64) -> i32 { @@ -11682,7 +11682,7 @@ pub unsafe fn Q6_p_not_any8_vcmpb_eq_PP(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[cfg_attr(test, assert_instr(dfadd))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11695,7 +11695,7 @@ pub unsafe fn Q6_P_dfadd_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[cfg_attr(test, assert_instr(dfsub))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11708,7 +11708,7 @@ pub unsafe fn Q6_P_dfsub_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[cfg_attr(test, assert_instr(mpyi))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11721,7 +11721,7 @@ pub unsafe fn Q6_R_mpyinac_RR(rx: i32, rs: i32, rt: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[rustc_legacy_const_generics(0, 1)] #[cfg_attr(test, assert_instr(mask, IU5 = 0, IU5_2 = 0))] @@ -11737,7 +11737,7 @@ pub unsafe fn Q6_R_mask_II() -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(clip, IU5 = 0))] @@ -11752,7 +11752,7 @@ pub unsafe fn Q6_R_clip_RI(rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(cround, IU6 = 0))] @@ -11767,7 +11767,7 @@ pub unsafe fn Q6_P_cround_PI(rss: i64) -> i64 { /// Instruction Type: S_3op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cround))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11780,7 +11780,7 @@ pub unsafe fn Q6_P_cround_PR(rss: i64, rt: i32) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(vclip, IU5 = 0))] @@ -11795,7 +11795,7 @@ pub unsafe fn Q6_P_vclip_PI(rss: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmax))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11808,7 +11808,7 @@ pub unsafe fn Q6_P_dfmax_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmin))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11821,7 +11821,7 @@ pub unsafe fn Q6_P_dfmin_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpyfix))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11834,7 +11834,7 @@ pub unsafe fn Q6_P_dfmpyfix_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpyhh))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11847,7 +11847,7 @@ pub unsafe fn Q6_P_dfmpyhhacc_PP(rxx: f64, rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpylh))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11860,7 +11860,7 @@ pub unsafe fn Q6_P_dfmpylhacc_PP(rxx: f64, rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpyll))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11873,7 +11873,7 @@ pub unsafe fn Q6_P_dfmpyll_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11886,7 +11886,7 @@ pub unsafe fn Q6_P_cmpyiw_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11899,7 +11899,7 @@ pub unsafe fn Q6_P_cmpyiwacc_PP(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11912,7 +11912,7 @@ pub unsafe fn Q6_P_cmpyiw_PP_conj(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11925,7 +11925,7 @@ pub unsafe fn Q6_P_cmpyiwacc_PP_conj(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11938,7 +11938,7 @@ pub unsafe fn Q6_P_cmpyrw_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11951,7 +11951,7 @@ pub unsafe fn Q6_P_cmpyrwacc_PP(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11964,7 +11964,7 @@ pub unsafe fn Q6_P_cmpyrw_PP_conj(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11977,7 +11977,7 @@ pub unsafe fn Q6_P_cmpyrwacc_PP_conj(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(vdmpyw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11990,7 +11990,7 @@ pub unsafe fn Q6_P_vdmpyw_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(vdmpyw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12003,7 +12003,7 @@ pub unsafe fn Q6_P_vdmpywacc_PP(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12016,7 +12016,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12029,7 +12029,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12042,7 +12042,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_conj_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12055,7 +12055,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_conj_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12068,7 +12068,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12081,7 +12081,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12094,7 +12094,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_conj_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12107,7 +12107,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_conj_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmlink))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12120,7 +12120,7 @@ pub unsafe fn Q6_dmlink_AA(rs: i32, rt: i32) { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmpause))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12133,7 +12133,7 @@ pub unsafe fn Q6_R_dmpause() -> i32 { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmpoll))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12146,7 +12146,7 @@ pub unsafe fn Q6_R_dmpoll() -> i32 { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmresume))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12159,7 +12159,7 @@ pub unsafe fn Q6_dmresume_A(rs: i32) { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmstart))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12172,7 +12172,7 @@ pub unsafe fn Q6_dmstart_A(rs: i32) { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmwait))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs index e05e19457319d..ec7693e27747a 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs @@ -147,30 +147,6 @@ unsafe extern "unadjusted" { fn __lasx_xvhsubw_wu_hu(a: __v16u16, b: __v16u16) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvhsubw.du.wu"] fn __lasx_xvhsubw_du_wu(a: __v8u32, b: __v8u32) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.b"] - fn __lasx_xvrepl128vei_b(a: __v32i8, b: u32) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.h"] - fn __lasx_xvrepl128vei_h(a: __v16i16, b: u32) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.w"] - fn __lasx_xvrepl128vei_w(a: __v8i32, b: u32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.d"] - fn __lasx_xvrepl128vei_d(a: __v4i64, b: u32) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvilvh.b"] - fn __lasx_xvilvh_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvilvh.h"] - fn __lasx_xvilvh_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvilvh.w"] - fn __lasx_xvilvh_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvilvh.d"] - fn __lasx_xvilvh_d(a: __v4i64, b: __v4i64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvilvl.b"] - fn __lasx_xvilvl_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvilvl.h"] - fn __lasx_xvilvl_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvilvl.w"] - fn __lasx_xvilvl_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvilvl.d"] - fn __lasx_xvilvl_d(a: __v4i64, b: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvpackev.b"] fn __lasx_xvpackev_b(a: __v32i8, b: __v32i8) -> __v32i8; #[link_name = "llvm.loongarch.lasx.xvpackev.h"] @@ -541,16 +517,6 @@ unsafe extern "unadjusted" { fn __lasx_xvstx(a: __v32i8, b: *mut i8, c: i64); #[link_name = "llvm.loongarch.lasx.xvextl.qu.du"] fn __lasx_xvextl_qu_du(a: __v4u64) -> __v4u64; - #[link_name = "llvm.loongarch.lasx.xvreplve0.b"] - fn __lasx_xvreplve0_b(a: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvreplve0.h"] - fn __lasx_xvreplve0_h(a: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvreplve0.w"] - fn __lasx_xvreplve0_w(a: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvreplve0.d"] - fn __lasx_xvreplve0_d(a: __v4i64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvreplve0.q"] - fn __lasx_xvreplve0_q(a: __v32i8) -> __v32i8; #[link_name = "llvm.loongarch.lasx.vext2xv.h.b"] fn __lasx_vext2xv_h_b(a: __v32i8) -> __v16i16; #[link_name = "llvm.loongarch.lasx.vext2xv.w.h"] @@ -1601,98 +1567,6 @@ pub fn lasx_xvhsubw_du_wu(a: m256i, b: m256i) -> m256i { unsafe { transmute(__lasx_xvhsubw_du_wu(transmute(a), transmute(b))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_b(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lasx_xvrepl128vei_b(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_h(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lasx_xvrepl128vei_h(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_w(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM2, 2); - unsafe { transmute(__lasx_xvrepl128vei_w(transmute(a), IMM2)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_d(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM1, 1); - unsafe { transmute(__lasx_xvrepl128vei_d(transmute(a), IMM1)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3062,41 +2936,6 @@ pub fn lasx_xvextl_qu_du(a: m256i) -> m256i { unsafe { transmute(__lasx_xvextl_qu_du(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_b(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_b(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_h(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_h(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_w(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_w(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_d(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_q(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_q(transmute(a))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs index 1d44f418bfbcd..aa4144cbb4da2 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs @@ -67,6 +67,144 @@ const unsafe fn simd_pickod_h(a: T, b: T) -> T { simd_shuffle!(b, a, [1, 3, 5, 7, 17, 19, 21, 23, 9, 11, 13, 15, 25, 27, 29, 31]) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_b(a: T, b: T) -> T { + simd_shuffle!( + b, + a, + [ + 8, 40, 9, 41, 10, 42, 11, 43, 12, 44, 13, 45, 14, 46, 15, 47, + 24, 56, 25, 57, 26, 58, 27, 59, 28, 60, 29, 61, 30, 62, 31, 63 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [4, 20, 5, 21, 6, 22, 7, 23, 12, 28, 13, 29, 14, 30, 15, 31]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [2, 10, 3, 11, 6, 14, 7, 15]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [1, 5, 3, 7]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_b(a: T, b: T) -> T { + simd_shuffle!( + b, + a, + [ + 0, 32, 1, 33, 2, 34, 3, 35, 4, 36, 5, 37, 6, 38, 7, 39, + 16, 48, 17, 49, 18, 50, 19, 51, 20, 52, 21, 53, 22, 54, 23, 55 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 16, 1, 17, 2, 18, 3, 19, 8, 24, 9, 25, 10, 26, 11, 27]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 8, 1, 9, 4, 12, 5, 13]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 4, 2, 6]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_b(a: T) -> T { + simd_shuffle!( + a, + a, + [ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, + I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, + I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_h(a: T) -> T { + simd_shuffle!( + a, + a, + [ + I, I, I, I, I, I, I, I, + I + 8, I + 8, I + 8, I + 8, I + 8, I + 8, I + 8, I + 8 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_w(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I, I + 4, I + 4, I + 4, I + 4]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_d(a: T) -> T { + simd_shuffle!(a, a, [I, I, I + 2, I + 2]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_b(a: T) -> T { + simd_shuffle!( + a, + a, + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_h(a: T) -> T { + simd_shuffle!(a, a, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_w(a: T) -> T { + simd_shuffle!(a, a, [0, 0, 0, 0, 0, 0, 0, 0]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_d(a: T) -> T { + simd_shuffle!(a, a, [0, 0, 0, 0]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_q(a: T) -> T { + simd_shuffle!(a, a, [0, 1, 0, 1]) +} + impl_vv!("lasx", lasx_xvpcnt_b, is::simd_ctpop, m256i, i8x32); impl_vv!("lasx", lasx_xvpcnt_h, is::simd_ctpop, m256i, i16x16); impl_vv!("lasx", lasx_xvpcnt_w, is::simd_ctpop, m256i, i32x8); @@ -81,6 +219,11 @@ impl_vv!("lasx", lasx_xvneg_w, is::simd_neg, m256i, i32x8); impl_vv!("lasx", lasx_xvneg_d, is::simd_neg, m256i, i64x4); impl_vv!("lasx", lasx_xvfsqrt_s, is::simd_fsqrt, m256, f32x8); impl_vv!("lasx", lasx_xvfsqrt_d, is::simd_fsqrt, m256d, f64x4); +impl_vv!("lasx", lasx_xvreplve0_b, simd_replve0_b, m256i, i8x32); +impl_vv!("lasx", lasx_xvreplve0_h, simd_replve0_h, m256i, i16x16); +impl_vv!("lasx", lasx_xvreplve0_w, simd_replve0_w, m256i, i32x8); +impl_vv!("lasx", lasx_xvreplve0_d, simd_replve0_d, m256i, i64x4); +impl_vv!("lasx", lasx_xvreplve0_q, simd_replve0_q, m256i, i64x4); impl_gv!("lasx", lasx_xvreplgr2vr_b, ls::simd_splat, m256i, i8x32, i32); impl_gv!("lasx", lasx_xvreplgr2vr_h, ls::simd_splat, m256i, i16x16, i32); @@ -230,6 +373,14 @@ impl_vvv!("lasx", lasx_xvpickod_b, simd_pickod_b, m256i, i8x32); impl_vvv!("lasx", lasx_xvpickod_h, simd_pickod_h, m256i, i16x16); impl_vvv!("lasx", lasx_xvpickod_w, simd_pickod_w, m256i, i32x8); impl_vvv!("lasx", lasx_xvpickod_d, simd_pickod_d, m256i, i64x4); +impl_vvv!("lasx", lasx_xvilvh_b, simd_ilvh_b, m256i, i8x32); +impl_vvv!("lasx", lasx_xvilvh_h, simd_ilvh_h, m256i, i16x16); +impl_vvv!("lasx", lasx_xvilvh_w, simd_ilvh_w, m256i, i32x8); +impl_vvv!("lasx", lasx_xvilvh_d, simd_ilvh_d, m256i, i64x4); +impl_vvv!("lasx", lasx_xvilvl_b, simd_ilvl_b, m256i, i8x32); +impl_vvv!("lasx", lasx_xvilvl_h, simd_ilvl_h, m256i, i16x16); +impl_vvv!("lasx", lasx_xvilvl_w, simd_ilvl_w, m256i, i32x8); +impl_vvv!("lasx", lasx_xvilvl_d, simd_ilvl_d, m256i, i64x4); impl_vuv!("lasx", lasx_xvslli_b, is::simd_shl, m256i, i8x32); impl_vuv!("lasx", lasx_xvslli_h, is::simd_shl, m256i, i16x16); @@ -263,6 +414,10 @@ impl_vuv!("lasx", lasx_xvmini_bu, cs::simd_imin, m256i, u8x32, 5); impl_vuv!("lasx", lasx_xvmini_hu, cs::simd_imin, m256i, u16x16, 5); impl_vuv!("lasx", lasx_xvmini_wu, cs::simd_imin, m256i, u32x8, 5); impl_vuv!("lasx", lasx_xvmini_du, cs::simd_imin, m256i, u64x4, 5); +impl_vuv!("lasx", lasx_xvrepl128vei_b, simd_replvei_b, m256i, i8x32, 4, const); +impl_vuv!("lasx", lasx_xvrepl128vei_h, simd_replvei_h, m256i, i16x16, 3, const); +impl_vuv!("lasx", lasx_xvrepl128vei_w, simd_replvei_w, m256i, i32x8, 2, const); +impl_vuv!("lasx", lasx_xvrepl128vei_d, simd_replvei_d, m256i, i64x4, 1, const); impl_vug!("lasx", lasx_xvpickve2gr_w, is::simd_extract, m256i, i32x8, i32, 3); impl_vug!("lasx", lasx_xvpickve2gr_d, is::simd_extract, m256i, i64x4, i64, 2); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs index 767be195292f2..9f13d2b3c43b4 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs @@ -155,30 +155,6 @@ unsafe extern "unadjusted" { fn __lsx_vreplve_w(a: __v4i32, b: i32) -> __v4i32; #[link_name = "llvm.loongarch.lsx.vreplve.d"] fn __lsx_vreplve_d(a: __v2i64, b: i32) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vreplvei.b"] - fn __lsx_vreplvei_b(a: __v16i8, b: u32) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vreplvei.h"] - fn __lsx_vreplvei_h(a: __v8i16, b: u32) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vreplvei.w"] - fn __lsx_vreplvei_w(a: __v4i32, b: u32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vreplvei.d"] - fn __lsx_vreplvei_d(a: __v2i64, b: u32) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vilvh.b"] - fn __lsx_vilvh_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vilvh.h"] - fn __lsx_vilvh_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vilvh.w"] - fn __lsx_vilvh_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vilvh.d"] - fn __lsx_vilvh_d(a: __v2i64, b: __v2i64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vilvl.b"] - fn __lsx_vilvl_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vilvl.h"] - fn __lsx_vilvl_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vilvl.w"] - fn __lsx_vilvl_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vilvl.d"] - fn __lsx_vilvl_d(a: __v2i64, b: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vpackev.b"] fn __lsx_vpackev_b(a: __v16i8, b: __v16i8) -> __v16i8; #[link_name = "llvm.loongarch.lsx.vpackev.h"] @@ -1541,98 +1517,6 @@ pub fn lsx_vreplve_d(a: m128i, b: i32) -> m128i { unsafe { transmute(__lsx_vreplve_d(transmute(a), transmute(b))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_b(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lsx_vreplvei_b(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_h(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lsx_vreplvei_h(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_w(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM2, 2); - unsafe { transmute(__lsx_vreplvei_w(transmute(a), IMM2)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_d(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM1, 1); - unsafe { transmute(__lsx_vreplvei_d(transmute(a), IMM1)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs index 24f9af851d8c1..2d24256838b21 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs @@ -53,6 +53,78 @@ const unsafe fn simd_pickod_d(a: T, b: T) -> T { simd_shuffle!(b, a, [1, 3]) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_b(a: T, b: T) -> T { + simd_shuffle!(b, a, [8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [4, 12, 5, 13, 6, 14, 7, 15]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [2, 6, 3, 7]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [1, 3]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_b(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 8, 1, 9, 2, 10, 3, 11]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 4, 1, 5]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 2]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_b(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_h(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I, I, I, I, I]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_w(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_d(a: T) -> T { + simd_shuffle!(a, a, [I, I]) +} + impl_vv!("lsx", lsx_vpcnt_b, is::simd_ctpop, m128i, i8x16); impl_vv!("lsx", lsx_vpcnt_h, is::simd_ctpop, m128i, i16x8); impl_vv!("lsx", lsx_vpcnt_w, is::simd_ctpop, m128i, i32x4); @@ -216,6 +288,14 @@ impl_vvv!("lsx", lsx_vpickod_b, simd_pickod_b, m128i, i8x16); impl_vvv!("lsx", lsx_vpickod_h, simd_pickod_h, m128i, i16x8); impl_vvv!("lsx", lsx_vpickod_w, simd_pickod_w, m128i, i32x4); impl_vvv!("lsx", lsx_vpickod_d, simd_pickod_d, m128i, i64x2); +impl_vvv!("lsx", lsx_vilvh_b, simd_ilvh_b, m128i, i8x16); +impl_vvv!("lsx", lsx_vilvh_h, simd_ilvh_h, m128i, i16x8); +impl_vvv!("lsx", lsx_vilvh_w, simd_ilvh_w, m128i, i32x4); +impl_vvv!("lsx", lsx_vilvh_d, simd_ilvh_d, m128i, i64x2); +impl_vvv!("lsx", lsx_vilvl_b, simd_ilvl_b, m128i, i8x16); +impl_vvv!("lsx", lsx_vilvl_h, simd_ilvl_h, m128i, i16x8); +impl_vvv!("lsx", lsx_vilvl_w, simd_ilvl_w, m128i, i32x4); +impl_vvv!("lsx", lsx_vilvl_d, simd_ilvl_d, m128i, i64x2); impl_vuv!("lsx", lsx_vslli_b, is::simd_shl, m128i, i8x16); impl_vuv!("lsx", lsx_vslli_h, is::simd_shl, m128i, i16x8); @@ -249,6 +329,10 @@ impl_vuv!("lsx", lsx_vmini_bu, cs::simd_imin, m128i, u8x16, 5); impl_vuv!("lsx", lsx_vmini_hu, cs::simd_imin, m128i, u16x8, 5); impl_vuv!("lsx", lsx_vmini_wu, cs::simd_imin, m128i, u32x4, 5); impl_vuv!("lsx", lsx_vmini_du, cs::simd_imin, m128i, u64x2, 5); +impl_vuv!("lsx", lsx_vreplvei_b, simd_replvei_b, m128i, i8x16, 4, const); +impl_vuv!("lsx", lsx_vreplvei_h, simd_replvei_h, m128i, i16x8, 3, const); +impl_vuv!("lsx", lsx_vreplvei_w, simd_replvei_w, m128i, i32x4, 2, const); +impl_vuv!("lsx", lsx_vreplvei_d, simd_replvei_d, m128i, i64x2, 1, const); impl_vug!("lsx", lsx_vpickve2gr_b, is::simd_extract, m128i, i8x16, i32, 4); impl_vug!("lsx", lsx_vpickve2gr_h, is::simd_extract, m128i, i16x8, i32, 3); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index f464dbd356b7f..e8bf098a33327 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -22,8 +22,20 @@ pub fn rdtime_d() -> (i64, isize) { #[allow(improper_ctypes)] unsafe extern "unadjusted" { + #[link_name = "llvm.loongarch.crc.w.b.w"] + fn __crc_w_b_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crc.w.h.w"] + fn __crc_w_h_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crc.w.w.w"] + fn __crc_w_w_w(a: i32, b: i32) -> i32; #[link_name = "llvm.loongarch.crc.w.d.w"] fn __crc_w_d_w(a: i64, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.b.w"] + fn __crcc_w_b_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.h.w"] + fn __crcc_w_h_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.w.w"] + fn __crcc_w_w_w(a: i32, b: i32) -> i32; #[link_name = "llvm.loongarch.crcc.w.d.w"] fn __crcc_w_d_w(a: i64, b: i32) -> i32; #[link_name = "llvm.loongarch.cacop.d"] @@ -48,6 +60,27 @@ unsafe extern "unadjusted" { fn __ldpte(a: i64, b: i64); } +/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crc_w_b_w(a: i8, b: i32) -> i32 { + unsafe { __crc_w_b_w(a as i32, b) } +} + +/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crc_w_h_w(a: i16, b: i32) -> i32 { + unsafe { __crc_w_h_w(a as i32, b) } +} + +/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crc_w_w_w(a: i32, b: i32) -> i32 { + unsafe { __crc_w_w_w(a, b) } +} + /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -55,6 +88,27 @@ pub fn crc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crc_w_d_w(a, b) } } +/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crcc_w_b_w(a: i8, b: i32) -> i32 { + unsafe { __crcc_w_b_w(a as i32, b) } +} + +/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crcc_w_h_w(a: i16, b: i32) -> i32 { + unsafe { __crcc_w_h_w(a as i32, b) } +} + +/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { + unsafe { __crcc_w_w_w(a, b) } +} + /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs index b4ec6881c36ab..24ee4874ae541 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs @@ -14,6 +14,7 @@ pub(super) const trait SimdExt: Sized { unsafe fn splat(v: i64) -> Self; } +#[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 macro_rules! impl_simd_ext { ($v:ident, $e:ty) => { #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] @@ -257,6 +258,20 @@ macro_rules! impl_vuv { } } }; + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ident, $ibs:expr, const) => { + #[inline] + #[target_feature(enable = $ft)] + #[rustc_legacy_const_generics(1)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub fn $name(a: $oty) -> $oty { + static_assert_uimm_bits!(IMM, $ibs); + unsafe { + let a: $ity = transmute(a); + let r: $ity = $op::(a); + transmute(r) + } + } + }; } pub(super) use impl_vuv; diff --git a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs index 948c98df61971..4f24ff2932210 100644 --- a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs @@ -22,18 +22,6 @@ pub fn rdtimeh_w() -> (i32, isize) { #[allow(improper_ctypes)] unsafe extern "unadjusted" { - #[link_name = "llvm.loongarch.crc.w.b.w"] - fn __crc_w_b_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crc.w.h.w"] - fn __crc_w_h_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crc.w.w.w"] - fn __crc_w_w_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crcc.w.b.w"] - fn __crcc_w_b_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crcc.w.h.w"] - fn __crcc_w_h_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crcc.w.w.w"] - fn __crcc_w_w_w(a: i32, b: i32) -> i32; #[link_name = "llvm.loongarch.dbar"] fn __dbar(a: i32); #[link_name = "llvm.loongarch.ibar"] @@ -70,48 +58,6 @@ unsafe extern "unadjusted" { fn __frsqrte_d(a: f64) -> f64; } -/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_b_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_b_w(a, b) } -} - -/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_h_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_h_w(a, b) } -} - -/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_w_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_w_w(a, b) } -} - -/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_b_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_b_w(a, b) } -} - -/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_h_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_h_w(a, b) } -} - -/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_w_w(a, b) } -} - /// Generates the memory barrier instruction #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/simd.rs b/library/stdarch/crates/core_arch/src/simd.rs index 2c6829b465c42..9a756eee446d2 100644 --- a/library/stdarch/crates/core_arch/src/simd.rs +++ b/library/stdarch/crates/core_arch/src/simd.rs @@ -87,6 +87,7 @@ impl Clone for Simd { } #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +#[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 impl const crate::cmp::PartialEq for Simd { #[inline] fn eq(&self, other: &Self) -> bool { @@ -299,6 +300,7 @@ impl Clone for SimdM { } #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +#[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 impl const crate::cmp::PartialEq for SimdM { #[inline] fn eq(&self, other: &Self) -> bool { diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt new file mode 100644 index 0000000000000..001538b3ea504 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt @@ -0,0 +1,31 @@ +# Bad LLVM codegen for BE in O2 in clang, and release in rust (https://github.com/llvm/llvm-project/issues/166190) +vcmla_laneq_f16 +vcmla_rot180_laneq_f16 +vcmla_rot270_laneq_f16 +vcmla_rot90_laneq_f16 +vcmlaq_lane_f16 +vcmlaq_laneq_f16 +vcmlaq_rot180_lane_f16 +vcmlaq_rot180_laneq_f16 +vcmlaq_rot270_lane_f16 +vcmlaq_rot270_laneq_f16 +vcmlaq_rot90_lane_f16 +vcmlaq_rot90_laneq_f16 + +# Bad codegen for BE in O2 in clang, correct in rust. Same cause as above issue. +vdot_lane_s32 +vdot_lane_u32 +vdot_laneq_s32 +vdot_laneq_u32 +vdotq_lane_s32 +vdotq_lane_u32 +vdotq_laneq_s32 +vdotq_laneq_u32 +vsudot_lane_s32 +vsudot_laneq_s32 +vsudotq_lane_s32 +vsudotq_laneq_s32 +vusdot_lane_s32 +vusdot_laneq_s32 +vusdotq_lane_s32 +vusdotq_laneq_s32 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_common.txt similarity index 93% rename from library/stdarch/crates/intrinsic-test/missing_aarch64.txt rename to library/stdarch/crates/intrinsic-test/missing_aarch64_be_common.txt index f0c9eeb6ce2c9..327c8207a0ff5 100644 --- a/library/stdarch/crates/intrinsic-test/missing_aarch64.txt +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_common.txt @@ -79,6 +79,3 @@ vcvtns_s64_f32 vcvtns_u64_f32 vcvtps_s64_f32 vcvtps_u64_f32 - -# Broken in Clang (fixed in https://github.com/llvm/llvm-project/pull/156029) -vcvth_s16_f16 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt new file mode 100644 index 0000000000000..eccd8a8a14dff --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt @@ -0,0 +1,20 @@ +# Broken in LLVM llvm/llvm-project#196999 +vmull_p64 +vmull_high_p64 + +# Broken in LLVM llvm/llvm-project#197083 +vcvth_n_s32_f16 +vcvth_n_u32_f16 +vcvth_n_s64_f16 +vcvth_n_u64_f16 +vcvth_n_f16_s32 +vcvth_n_f16_u32 +vcvth_n_f16_s64 +vcvth_n_f16_u64 + +# Broken in GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125279 +vmaxh_f16 +vminh_f16 + +# Rounding errors +vfms_n_f64 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_clang.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_clang.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_be.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_common.txt similarity index 57% rename from library/stdarch/crates/intrinsic-test/missing_aarch64_be.txt rename to library/stdarch/crates/intrinsic-test/missing_aarch64_common.txt index 9163aaa1c8db0..327c8207a0ff5 100644 --- a/library/stdarch/crates/intrinsic-test/missing_aarch64_be.txt +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_common.txt @@ -1,43 +1,3 @@ -# Bad LLVM codegen for BE in O2 in clang, and release in rust (https://github.com/llvm/llvm-project/issues/166190) -vcmla_lane_f16 -vcmla_laneq_f16 -vcmla_rot180_lane_f16 -vcmla_rot180_laneq_f16 -vcmla_rot270_lane_f16 -vcmla_rot270_laneq_f16 -vcmla_rot90_lane_f16 -vcmla_rot90_laneq_f16 -vcmlaq_lane_f16 -vcmlaq_laneq_f16 -vcmlaq_laneq_f32 -vcmlaq_rot180_lane_f16 -vcmlaq_rot180_laneq_f16 -vcmlaq_rot180_laneq_f32 -vcmlaq_rot270_lane_f16 -vcmlaq_rot270_laneq_f16 -vcmlaq_rot270_laneq_f32 -vcmlaq_rot90_lane_f16 -vcmlaq_rot90_laneq_f16 -vcmlaq_rot90_laneq_f32 -# Bad codegen for BE in O2 in clang, correct in rust. Same cause as above issue. -vdot_lane_s32 -vdot_lane_u32 -vdot_laneq_s32 -vdot_laneq_u32 -vdotq_lane_s32 -vdotq_lane_u32 -vdotq_laneq_s32 -vdotq_laneq_u32 -vsudot_lane_s32 -vsudot_laneq_s32 -vsudotq_lane_s32 -vsudotq_laneq_s32 -vusdot_lane_s32 -vusdot_laneq_s32 -vusdotq_lane_s32 -vusdotq_laneq_s32 - -# Below are in common to missing_aarch64.txt # Not supported by qemu (will throw illegal instruction) vamin_f16 vaminq_f16 @@ -119,6 +79,3 @@ vcvtns_s64_f32 vcvtns_u64_f32 vcvtps_s64_f32 vcvtps_u64_f32 - -# Broken in Clang -vcvth_s16_f16 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt new file mode 100644 index 0000000000000..a7e90142e4bcd --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt @@ -0,0 +1,19 @@ +# Broken in GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123584, fixed in GCC 16 +vxarq_u64 + +# Broken in LLVM llvm/llvm-project#197083 +vcvth_n_s32_f16 +vcvth_n_u32_f16 +vcvth_n_s64_f16 +vcvth_n_u64_f16 +vcvth_n_f16_s32 +vcvth_n_f16_u32 +vcvth_n_f16_s64 +vcvth_n_f16_u64 + +# Broken in GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125279 +vmaxh_f16 +vminh_f16 + +# Rounding errors +vfms_n_f64 diff --git a/library/stdarch/crates/intrinsic-test/missing_arm_clang.txt b/library/stdarch/crates/intrinsic-test/missing_arm_clang.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/library/stdarch/crates/intrinsic-test/missing_arm.txt b/library/stdarch/crates/intrinsic-test/missing_arm_common.txt similarity index 100% rename from library/stdarch/crates/intrinsic-test/missing_arm.txt rename to library/stdarch/crates/intrinsic-test/missing_arm_common.txt diff --git a/library/stdarch/crates/intrinsic-test/missing_x86_clang.txt b/library/stdarch/crates/intrinsic-test/missing_x86_clang.txt new file mode 100644 index 0000000000000..b1531830c1a19 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_x86_clang.txt @@ -0,0 +1,24 @@ +# not present in Clang +_bswap +_bswap64 +_mm_cvtsd_si64x +_mm_cvtsi128_si64x +_mm_cvtsi64x_sd +_mm_cvtsi64x_si128 +_mm_cvttsd_si64x +_popcnt32 +_popcnt64 + +# Clang bug +_mm512_mask_reduce_max_pd +_mm512_mask_reduce_max_ps +_mm512_mask_reduce_min_pd +_mm512_mask_reduce_min_ps + +# Rounding errors in release mode +_mm_maskz_fmadd_sd +_mm_maskz_fmadd_ss +_mm_maskz_fmsub_sd +_mm_maskz_fmsub_ss +_mm_maskz_fnmadd_sd +_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/missing_x86.txt b/library/stdarch/crates/intrinsic-test/missing_x86_common.txt similarity index 59% rename from library/stdarch/crates/intrinsic-test/missing_x86.txt rename to library/stdarch/crates/intrinsic-test/missing_x86_common.txt index c7aabb95a84b0..f9b71bbe8c2ca 100644 --- a/library/stdarch/crates/intrinsic-test/missing_x86.txt +++ b/library/stdarch/crates/intrinsic-test/missing_x86_common.txt @@ -1,19 +1,22 @@ -# Are defined under a similar name - -#__bswap_64 -_bswap64 - -# not present in Clang and Rust +# not present in Rust _bit_scan_forward _bit_scan_reverse _castf32_u32 _castf64_u64 _castu32_f32 _castu64_f64 +_cvtsh_ss +_cvtss_sh _lrotl _lrotr _may_i_use_cpu_feature _may_i_use_cpu_feature_ext +_mm256_set1_pch +_mm512_set1_pch +_mm_malloc +_mm_popcnt_u32 +_mm_popcnt_u64 +_mm_set1_pch _rdpmc _rotl _rotl64 @@ -21,29 +24,9 @@ _rotr _rotr64 _rotwl _rotwr -_urdmsr - -# not present in Clang -_bswap -_mm_cvtsd_si64x -_mm_cvtsi128_si64x -_mm_cvtsi64x_sd -_mm_cvtsi64x_si128 -_mm_cvttsd_si64x -_popcnt32 -_popcnt64 - -# not present in Rust -_cvtsh_ss -_cvtss_sh -_mm256_set1_pch -_mm512_set1_pch -_mm_malloc -_mm_popcnt_u32 -_mm_popcnt_u64 -_mm_set1_pch _tpause _umwait +_urdmsr # SDE ERROR: Cannot execute XGETBV with ECX != 0 _xgetbv @@ -63,17 +46,3 @@ _mm512_castph256_ph512 _mm512_castps256_ps512 _mm512_castpd256_pd512 _mm512_castsi256_si512 - -# Clang bug -_mm512_mask_reduce_max_pd -_mm512_mask_reduce_max_ps -_mm512_mask_reduce_min_pd -_mm512_mask_reduce_min_ps - -# Rounding errors in release mode -_mm_maskz_fmadd_sd -_mm_maskz_fmadd_ss -_mm_maskz_fmsub_sd -_mm_maskz_fmsub_ss -_mm_maskz_fnmadd_sd -_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt new file mode 100644 index 0000000000000..5b71b0698eafc --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt @@ -0,0 +1,33 @@ +# not present in GCC +_bextr2_u32 +_bextr2_u64 +_mm512_cvtepi32lo_pd +_mm512_mask_cvtepi32lo_pd +_mm512_cvtepu32lo_pd +_mm512_mask_cvtepu32lo_pd +_mm512_cvtpd_pslo +_mm512_mask_cvtpd_pslo +_mm512_cvtpslo_pd +_mm512_mask_cvtpslo_pd +_mm512_permutevar_epi32 +_mm512_mask_permutevar_epi32 +_mm_tzcnt_32 +_mm_tzcnt_64 + +# GCC bug +_mm512_reduce_max_pd +_mm512_reduce_max_ps +_mm512_reduce_min_pd +_mm512_reduce_min_ps +_mm512_mask_reduce_max_pd +_mm512_mask_reduce_max_ps +_mm512_mask_reduce_min_pd +_mm512_mask_reduce_min_ps + +# Rounding errors in release mode +_mm_maskz_fmadd_sd +_mm_maskz_fmadd_ss +_mm_maskz_fmsub_sd +_mm_maskz_fmsub_ss +_mm_maskz_fnmadd_sd +_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/missing_x86_icx.txt b/library/stdarch/crates/intrinsic-test/missing_x86_icx.txt new file mode 100644 index 0000000000000..6d3133c85db9c --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_x86_icx.txt @@ -0,0 +1,20 @@ +# not present in ICX +_mm_cvtsd_si64x +_mm_cvtsi128_si64x +_mm_cvtsi64x_sd +_mm_cvtsi64x_si128 +_mm_cvttsd_si64x + +# ICX bug +_mm512_mask_reduce_max_pd +_mm512_mask_reduce_max_ps +_mm512_mask_reduce_min_pd +_mm512_mask_reduce_min_ps + +# Rounding errors in release mode +_mm_maskz_fmadd_sd +_mm_maskz_fmadd_ss +_mm_maskz_fmsub_sd +_mm_maskz_fmsub_ss +_mm_maskz_fnmadd_sd +_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/src/arm/argument.rs b/library/stdarch/crates/intrinsic-test/src/arm/argument.rs deleted file mode 100644 index c43609bb2db0d..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/arm/argument.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::arm::intrinsic::ArmIntrinsicType; -use crate::common::argument::Argument; - -// This functionality is present due to the nature -// of how intrinsics are defined in the JSON source -// of ARM intrinsics. -impl Argument { - pub fn type_and_name_from_c(arg: &str) -> (&str, &str) { - let split_index = arg - .rfind([' ', '*']) - .expect("Couldn't split type and argname"); - - (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index 29343bee4c300..a54e5857192e0 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,21 +2,18 @@ use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] -pub struct ArmIntrinsicType { - pub data: IntrinsicType, - pub target: String, -} +pub struct ArmIntrinsicType(pub IntrinsicType); impl Deref for ArmIntrinsicType { type Target = IntrinsicType; fn deref(&self) -> &Self::Target { - &self.data + &self.0 } } impl DerefMut for ArmIntrinsicType { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data + &mut self.0 } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index c1563a7364ce7..06cf78a422285 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,8 +1,9 @@ use super::intrinsic::ArmIntrinsicType; +use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::IntrinsicType; +use crate::common::intrinsic_helpers::{IntrinsicType, TypeKind}; use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; @@ -58,7 +59,6 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, - target: &str, ) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); @@ -68,7 +68,7 @@ pub fn get_neon_intrinsics( .into_iter() .filter_map(|intr| { if intr.simd_isa == "Neon" { - Some(json_to_intrinsic(intr, target).expect("Couldn't parse JSON")) + Some(json_to_intrinsic(intr).expect("Couldn't parse JSON")) } else { None } @@ -79,32 +79,58 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, - target: &str, ) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?; + let result_ty = ArmIntrinsicType(parse_intrinsic_type(&intr.return_type.value)?); let args = intr .arguments .into_iter() .enumerate() .map(|(i, arg)| { - let (type_name, arg_name) = Argument::::type_and_name_from_c(&arg); + let (type_name, arg_name) = { + let split_index = arg + .rfind([' ', '*']) + .expect("Couldn't split type and argname"); + + (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) + }; + + let arg_ty = parse_intrinsic_type(type_name) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + let metadata = intr.args_prep.as_mut(); let metadata = metadata.and_then(|a| a.remove(arg_name)); let arg_prep: Option = metadata.and_then(|a| a.try_into().ok()); - let constraint: Option = arg_prep.and_then(|a| a.try_into().ok()); - let ty = ArmIntrinsicType::from_c(type_name, target) - .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); - - let mut arg = - Argument::::new(i, String::from(arg_name), ty, constraint); + let constraint: Option = + arg_prep.and_then(|a| a.try_into().ok()).or_else(|| { + if arg_ty.kind() == TypeKind::SvPattern { + Some(Constraint::SvPattern) + } else if arg_ty.kind() == TypeKind::SvPrefetchOp { + Some(Constraint::SvPrefetchOp) + } else if arg_name == "imm_rotation" { + if name.starts_with("svcadd_") || name.starts_with("svqcadd_") { + Some(Constraint::SvImmRotationAdd) + } else { + Some(Constraint::SvImmRotation) + } + } else { + None + } + }); + + let mut arg = Argument::::new( + i, + String::from(arg_name), + ArmIntrinsicType(arg_ty), + constraint, + ); // The JSON doesn't list immediates as const let IntrinsicType { ref mut constant, .. - } = arg.ty.data; + } = *arg.ty; if arg.name.starts_with("imm") { *constant = true } @@ -117,7 +143,7 @@ fn json_to_intrinsic( Ok(Intrinsic { name, arguments, - results, + results: result_ty, arch_tags: intr.architectures, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 9bf6c95ffdcb3..378f23ba7c361 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,11 +1,10 @@ -mod argument; mod config; mod intrinsic; mod json_parser; mod types; use crate::common::SupportedArchitectureTest; -use crate::common::cli::ProcessedCli; +use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use intrinsic::ArmIntrinsicType; @@ -29,14 +28,18 @@ impl SupportedArchitectureTest for ArmArchitectureTest { const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - fn arch_flags(&self) -> Vec<&str> { - vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"] + fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { + // GCC uses an extra `-` in the arch name + match cli_options.cc_arg_style { + CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], + CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], + } } - fn create(cli_options: ProcessedCli) -> Self { + fn create(cli_options: &ProcessedCli) -> Self { let a32 = cli_options.target.starts_with("armv7"); - let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) - .expect("Error parsing input file"); + let mut intrinsics = + get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); intrinsics.dedup(); @@ -46,15 +49,19 @@ impl SupportedArchitectureTest for ArmArchitectureTest { let intrinsics = intrinsics .into_iter() - // Not sure how we would compare intrinsic that returns void. + // Skip intrinsics that don't return a value. .filter(|i| i.results.kind() != TypeKind::Void) + // Skip bfloat intrinsics - not currently supported .filter(|i| i.results.kind() != TypeKind::BFloat) .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat)) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) + // Skip intrinsics with 128-bit elements (e.g. `p128`) .filter(|i| !i.arguments.iter().any(|a| a.ty.inner_size() == 128)) + // Skip intrinsics from `--skip` .filter(|i| !cli_options.skip.contains(&i.name)) + // Skip A64-specific intrinsics on A32 .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()])) .take(sample_size) .collect::>(); diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index e9614eba218cb..cd420f10678fc 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,7 @@ use super::intrinsic::ArmIntrinsicType; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::{ + IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, +}; impl IntrinsicTypeDefinition for ArmIntrinsicType { /// Gets a string containing the typename for this type in C format. @@ -9,8 +11,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let Some(bit_len) = self.bit_len { match (self.simd_len, self.vec_len) { (None, None) => format!("{prefix}{bit_len}_t"), - (Some(simd), None) => format!("{prefix}{bit_len}x{simd}_t"), - (Some(simd), Some(vec)) => format!("{prefix}{bit_len}x{simd}x{vec}_t"), + (Some(SimdLen::Fixed(simd)), None) => format!("{prefix}{bit_len}x{simd}_t"), + (Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{prefix}{bit_len}x{simd}x{vec}_t") + } + (Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), + (Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{prefix}{bit_len}x{vec}_t") + } (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case } } else { @@ -25,8 +33,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let Some(bit_len) = self.bit_len { match (self.simd_len, self.vec_len) { (None, None) => format!("{rust_prefix}{bit_len}"), - (Some(simd), None) => format!("{c_prefix}{bit_len}x{simd}_t"), - (Some(simd), Some(vec)) => format!("{c_prefix}{bit_len}x{simd}x{vec}_t"), + (Some(SimdLen::Fixed(simd)), None) => format!("{c_prefix}{bit_len}x{simd}_t"), + (Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{c_prefix}{bit_len}x{simd}x{vec}_t") + } + (Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), + (Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{c_prefix}{bit_len}x{vec}_t") + } (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case } } else { @@ -39,16 +53,11 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let IntrinsicType { kind: k, bit_len: Some(bl), - simd_len, vec_len, .. - } = &self.data + } = **self { - let quad = if simd_len.unwrap_or(1) * bl > 64 { - "q" - } else { - "" - }; + let quad = if self.num_lanes() * bl > 64 { "q" } else { "" }; format!( "vld{len}{quad}_{type}{size}", @@ -69,79 +78,99 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { } } -impl ArmIntrinsicType { - pub fn from_c(s: &str, target: &str) -> Result { - const CONST_STR: &str = "const"; - if let Some(s) = s.strip_suffix('*') { - let (s, constant) = match s.trim().strip_suffix(CONST_STR) { - Some(stripped) => (stripped, true), - None => (s, false), - }; - let s = s.trim_end(); - let temp_return = ArmIntrinsicType::from_c(s, target); - temp_return.map(|mut op| { - op.ptr = true; - op.ptr_constant = constant; - op - }) +pub fn parse_intrinsic_type(s: &str) -> Result { + const CONST_STR: &str = "const"; + const ENUM_STR: &str = "enum "; + + // Recurse to handle pointers.. + if let Some(s) = s.strip_suffix('*') { + let s = s.trim(); + let (s, constant) = if s.ends_with(CONST_STR) || s.starts_with(CONST_STR) { + ( + s.trim_start_matches(CONST_STR).trim_end_matches(CONST_STR), + true, + ) } else { - // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] - let (mut s, constant) = match s.strip_prefix(CONST_STR) { - Some(stripped) => (stripped.trim(), true), - None => (s, false), - }; - s = s.strip_suffix("_t").unwrap_or(s); - let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] - let start = parts.next().ok_or("Impossible to parse type")?; - if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { - let (arg_kind, bit_len) = start.split_at(digit_start); - let arg_kind = arg_kind.parse::()?; - let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; - let simd_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse simd_len: {part}")?, - ), - None => None, - }; - let vec_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse vec_len: {part}")?, - ), - None => None, - }; - Ok(ArmIntrinsicType { - data: IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: arg_kind, - bit_len: Some(bit_len), - simd_len, - vec_len, - }, - target: target.to_string(), - }) - } else { - let kind = start.parse::()?; - let bit_len = match kind { - TypeKind::Int(_) => Some(32), - _ => None, - }; - Ok(ArmIntrinsicType { - data: IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: start.parse::()?, - bit_len, - simd_len: None, - vec_len: None, - }, - target: target.to_string(), - }) - } - } + (s, false) + }; + + let mut ty = parse_intrinsic_type(s.trim())?; + ty.ptr = true; + ty.ptr_constant = constant; + return Ok(ty); } + + // [const ][sv]TYPE[{element_bits}[x{num_lanes}[x{num_vecs}]]][_t] + // | [enum ]TYPE + let (mut s, constant) = match (s.strip_prefix(CONST_STR), s.strip_prefix(ENUM_STR)) { + (Some(const_strip), _) => (const_strip, true), + (_, Some(enum_strip)) => (enum_strip, true), + (None, None) => (s, false), + }; + s = s.trim(); + s = s.strip_suffix("_t").unwrap_or(s); + + // Consider the following types as examples: + // A) `svuint32x3_t` + // B) `float16x4x2_t` + // C) `svbool_t` + + let sve = s.starts_with("sv"); + + let mut parts = s.split('x'); + let start = parts.next().ok_or("failed to parse type")?; + + // Continuing the previous examples.. + // A) kind=TypeKind::Int(Sign::Unsigned), bit_len=Some(32) + // B) kind=TypeKind::Float, bit_len=Some(16) + // C) kind=TypeKind::Bool, bit_len=None + let (kind, bit_len) = if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { + let (element_kind, element_bits) = start.split_at(digit_start); + let element_kind = element_kind.parse::()?; + let element_bits = element_bits.parse::().map_err(|err| err.to_string())?; + (element_kind, Some(element_bits)) + } else { + let element_kind = start.parse::()?; + (element_kind, None) + }; + + let bit_len = match (bit_len, kind) { + (None, TypeKind::SvPattern | TypeKind::SvPrefetchOp | TypeKind::Int(_)) => Some(32), + (None, TypeKind::Bool) => Some(8), + _ => bit_len, + }; + + // Continuing the previous examples.. + // A) second_len=Some(3) + // B) second_len=Some(4) + // C) second_len=None + let second_len = parts.next().map(|part| { + part.parse::() + .expect("failed to parse second part of type") + }); + + // Continuing the previous examples.. + // A) third_len=None + // B) third_len=Some(2) + // C) third_len=None + let third_len = parts.next().map(|part| { + part.parse::() + .expect("failed to parse third part of type") + }); + + let (simd_len, vec_len) = if sve { + (Some(SimdLen::Scalable), second_len) + } else { + (second_len.map(SimdLen::Fixed), third_len) + }; + + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind, + bit_len, + simd_len, + vec_len, + }) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 885d5e998ef54..25207a8c458fd 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -4,7 +4,6 @@ use crate::common::intrinsic_helpers::TypeKind; use super::constraint::Constraint; use super::gen_rust::PASSES; -use super::indentation::Indentation; use super::intrinsic_helpers::IntrinsicTypeDefinition; /// An argument for the intrinsic. @@ -53,7 +52,8 @@ where self.constraint.is_some() } - /// The name (e.g. "A_VALS" or "a_vals") for the array of possible test inputs. + /// Returns a string with the name of the static variable containing test values for intrinsic + /// arguments of this type. pub(crate) fn rust_vals_array_name(&self) -> impl std::fmt::Display { let loads = crate::common::gen_rust::PASSES; format!( @@ -63,12 +63,15 @@ where ) } + /// Should this argument be passed by reference in C wrapper function declarations? + /// + /// SIMD types and `f16` are currently passed by reference. pub(crate) fn pass_by_ref(&self) -> bool { - // pass SIMD types and `f16` by reference self.is_simd() || (self.ty.kind() == TypeKind::Float && self.ty.inner_size() == 16) } } +/// Arguments of an intrinsic - including parameters that end up being const generics. #[derive(Debug, PartialEq, Clone)] pub struct ArgumentList { pub args: Vec>, @@ -78,6 +81,11 @@ impl ArgumentList where T: IntrinsicTypeDefinition, { + /// Returns a string with the arguments in `self` as a parameter list for a wrapper fn + /// definition in C (e.g. `$ty1 $arg1, $ty2 $arg2`). + /// + /// Skips arguments with constraints - which correspond to arguments that must take immediates - + /// as a different C definition will be generated for each value of these being tested. pub fn as_non_imm_arglist_c(&self) -> String { self.iter() .filter(|arg| !arg.has_constraint()) @@ -91,6 +99,11 @@ where .to_string() } + /// Returns a string with the arguments in `self` as a parameter list for a Rust declaration of + /// a C wrapper fn (e.g. `$arg1: $ty1, $arg2: $ty2`). + /// + /// Skips arguments with constraints - which correspond to arguments that must take immediates - + /// as a different C definition will be generated for each value of these being tested. pub fn as_non_imm_arglist_rust(&self) -> String { self.iter() .filter(|arg| !arg.has_constraint()) @@ -108,6 +121,8 @@ where .to_string() } + /// Returns a string with the arguments in `self` being passed to an intrinsic call in C + /// (e.g. `$arg1, 2 /* imm_args[0] */, $arg3` where `$arg2` has a constraint). pub fn as_call_params_c(&self, imm_args: &[i64]) -> String { let mut imm_args = imm_args.iter(); self.iter() @@ -124,8 +139,9 @@ where .to_string() } - /// Converts the argument list into the call parameters for a Rust function. - /// e.g. this would generate something like `a, b, c` + /// Returns a string with the arguments in `self` being passed to an intrinsic call in Rust. + /// (e.g. `$arg1, $arg3` where `$arg2` has a constraint and so corresponds to a const generic + /// parameter). pub fn as_call_param_rust(&self) -> String { self.iter() .filter(|a| !a.has_constraint()) @@ -133,6 +149,9 @@ where .join(", ") } + /// Returns a string with the arguments in `self` being passed to the declaration of a C wrapper + /// fn from Rust (e.g. `$arg1, $arg3` (where `$arg2` has a constraint and so corresponds to a + /// const generic parameter). pub fn as_c_call_param_rust(&self) -> String { self.iter() .filter(|a| !a.has_constraint()) @@ -146,40 +165,73 @@ where .join("") } + /// Returns a string defining a static variable with test values used for all intrinsics with + /// arguments of `arg`'s type. + /// + /// e.g. + /// ```rust,ignore + /// static U8_20: [u8; 20] = [ + /// 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xf0, + /// 0x80, 0x3b, 0xff, + /// ]; + /// ``` + /// + /// `num_lanes * num_vectors + loads - 1` elements are present in the array, which is sufficient + /// for a `loads` number of `num_lanes * num_vectors` windows into the array to be loaded: + /// + /// ```text + /// [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xf0, 0x80, 0x3b, 0xff] + /// ^^^^^^^^^^^^^^^^^^^ first window of `num_lanes * num_vectors` elements (e.g. four elements) + /// ^^^^^^^^^^^^^^^^^^ second window + /// `loads`th window ^^^^^^^^^^^^^^^^^^^^^^ + /// ``` pub fn gen_arg_rust( arg: &Argument, w: &mut impl std::io::Write, - indentation: Indentation, loads: u32, ) -> std::io::Result<()> { writeln!( w, - "{indentation}static {name}: [{ty}; {load_size}] = {values};\n", + "static {name}: [{ty}; {load_size}] = {values};\n", name = arg.rust_vals_array_name(), ty = arg.ty.rust_scalar_type(), load_size = arg.ty.num_lanes() * arg.ty.num_vectors() + loads - 1, - values = arg.ty.populate_random(indentation, loads) + values = arg.ty.populate_random(loads) ) } - /// Creates a line for each argument that initializes the argument from array `[ARG]_VALS` at - /// an offset `i` using a load intrinsic, in Rust. - /// e.g `let a = vld1_u8(A_VALS.as_ptr().offset(i));` - pub fn load_values_rust(&self, indentation: Indentation) -> String { + /// Returns a string defining a local variable for each argument and loading a value into each + /// using a load intrinsic. + /// + /// e.g. + /// ```rust,ignore + /// let a = vld1_u8(I16_23.as_ptr().offset((i + 0 /* idx */) % 20 /* PASSES */)); + /// ```` + /// + /// The generator will have already generated arrays of appropriate length with values that can + /// be used for testing (see the `gen_args_rust` function). + /// + /// Each load is assumed to have a variable `i` in scope which comes from a loop which repeats + /// the testing of the intrinsic for different values - each subsequent `i` shifts the window + /// of values being loaded along the pre-prepared array. + /// + /// Each subsequent argument's first window is started one element further into the array + /// then the previous. + pub fn load_values_rust(&self) -> String { self.iter() .filter(|&arg| !arg.has_constraint()) .enumerate() .map(|(idx, arg)| { if arg.is_simd() { format!( - "{indentation}let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", name = arg.generate_name(), vals_name = arg.rust_vals_array_name(), load = arg.ty.get_load_function(), ) } else { format!( - "{indentation}let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", + "let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", name = arg.generate_name(), vals_name = arg.rust_vals_array_name(), ) @@ -188,6 +240,7 @@ where .collect() } + /// Returns an iterator over the contained arguments pub fn iter(&self) -> std::slice::Iter<'_, Argument> { self.args.iter() } diff --git a/library/stdarch/crates/intrinsic-test/src/common/cli.rs b/library/stdarch/crates/intrinsic-test/src/common/cli.rs index f407b5ceb7d48..3ea87df51fda2 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/cli.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/cli.rs @@ -13,14 +13,25 @@ pub struct Cli { /// Filename for a list of intrinsics to skip (one per line) #[arg(long)] - pub skip: Option, + pub skip: Vec, /// Pass a target the test suite #[arg(long)] pub target: String, + /// Percentage of intrinsics to test (used to limit testing to keep CI times manageable) #[arg(long, default_value_t = 100u8)] pub sample_percentage: u8, + + /// Argument style of the C compiler + #[arg(long)] + pub cc_arg_style: CcArgStyle, +} + +#[derive(Copy, Clone, clap::ValueEnum)] +pub enum CcArgStyle { + Gcc, + Clang, } pub struct ProcessedCli { @@ -28,6 +39,7 @@ pub struct ProcessedCli { pub target: String, pub skip: Vec, pub sample_percentage: u8, + pub cc_arg_style: CcArgStyle, } impl ProcessedCli { @@ -36,22 +48,25 @@ impl ProcessedCli { let target = cli_options.target; let sample_percentage = cli_options.sample_percentage; - let skip = if let Some(filename) = cli_options.skip { - let data = std::fs::read_to_string(&filename).expect("Failed to open file"); - data.lines() - .map(str::trim) - .filter(|s| !s.contains('#')) - .map(String::from) - .collect_vec() - } else { - Default::default() - }; + let skip = cli_options + .skip + .iter() + .flat_map(|filename| { + std::fs::read_to_string(&filename) + .expect("Failed to open file") + .lines() + .map(|line| line.trim().to_owned()) + .filter(|line| !line.contains('#')) + .collect_vec() + }) + .collect_vec(); Self { target, skip, filename, sample_percentage, + cc_arg_style: cli_options.cc_arg_style, } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs index 5984e0fcc22f9..ab52d866ab20a 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs @@ -1,24 +1,43 @@ use serde::Deserialize; use std::ops::Range; -/// Describes the values to test for a const generic parameter. +/// Describes the values to test for a const generic parameter #[derive(Debug, PartialEq, Clone, Deserialize)] pub enum Constraint { - /// Test a single value. + /// Test a single value Equal(i64), /// Test a range of values, e.g. `0..16`. Range(Range), /// Test discrete values, e.g. `vec![1, 2, 4, 8]`. Set(Vec), + /// Values of `core::arch::aarch64::svpattern` + SvPattern, + /// Values of `core::arch::aarch64::svprfop` + SvPrefetchOp, + // Values of the `imm_rotation` argument in SVE intrinsics where arguments contain complex + // pairs and `imm_rotation` corresponds to the rotation. + SvImmRotation, + // Values of the `imm_rotation` argument in SVE intrinsics where arguments contain complex + // pairs and `imm_rotation` corresponds to the rotation (this variant is specifically for + // `svcadd` and `svqcadd` where only 90 and 270 are valid arguments). + SvImmRotationAdd, } impl Constraint { - /// Iterate over the values of this constraint. - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + /// Returns an iterator over the values of this constraint + pub fn iter(&self) -> Box + '_> { match self { - Constraint::Equal(i) => std::slice::Iter::default().copied().chain(*i..*i + 1), - Constraint::Range(range) => std::slice::Iter::default().copied().chain(range.clone()), - Constraint::Set(items) => items.iter().copied().chain(std::ops::Range::default()), + Constraint::Equal(i) => Box::new(std::iter::once(*i)), + Constraint::Range(range) => Box::new(range.clone()), + Constraint::Set(items) => Box::new(items.iter().copied().chain(Range::default())), + // These values are discriminants of the `svpattern` enum + Constraint::SvPattern => Box::new((0..=13).chain(29..=31)), + // These values are discriminants of the `svprfop` enum + Constraint::SvPrefetchOp => Box::new((0..=5).chain(8..=14)), + // Valid rotations for intrinsics operating on complex pairs: 0, 90, 180, 270 + Constraint::SvImmRotation => Box::new((0..=270).step_by(90)), + // Valid rotations for `svcadd` and `svqcadd`: 0, 270 + Constraint::SvImmRotationAdd => Box::new((90..=270).step_by(180)), } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index bdf6f68d58cc2..24756324c48e4 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -4,6 +4,16 @@ use crate::common::intrinsic::Intrinsic; use super::intrinsic_helpers::IntrinsicTypeDefinition; +/// Generates a C source file containing wrapper functions around each specialisation of each +/// intrinsic (that is, intrinsics with specific values for the the immediate arguments). Each +/// wrapper function is invoked via FFI from the Rust binary doing the testing. +/// +/// e.g. +/// ```c +/// void __crc32cd_wrapper(uint32_t* __dst, uint32_t a, uint64_t b) { +/// *__dst = __crc32cd(a, b); +/// } +/// ``` pub fn write_wrapper_c( w: &mut impl std::io::Write, notice: &str, diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index d23710451d478..cb07fa600474c 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -1,15 +1,20 @@ +use std::process::Command; + use itertools::Itertools; -use super::indentation::Indentation; use super::intrinsic_helpers::IntrinsicTypeDefinition; use crate::common::argument::ArgumentList; +use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; -// The number of times each intrinsic will be called. +// The number of times each intrinsic will be called - influences the generation of the +// test arrays to minimise repeated testing of the same test values. pub(crate) const PASSES: u32 = 20; -// we need a reflexive equality relation, so treat NaNs as equal +/// Rust definitions that are included verbatim in the generated source. In particular, defines +/// a wrapper around float types that defines `NaN`s to be equal reflexively to enable +/// comparison of results that use floats types. const COMMON_RUST_DEFINITIONS: &str = r#" macro_rules! wrap_partialeq { ($($wrapper:ident ($inner:ty)),*) => {$( @@ -36,6 +41,29 @@ macro_rules! concatln { }; } +/// Run rustfmt on the generated source code +pub fn run_rustfmt(source_path: &str) { + let output = Command::new("rustfmt") + .args([source_path]) + .output() + .expect("failed to run rustfmt on generated sources"); + + if !output.status.success() { + panic!( + "failed to run rustfmt on generated sources:\nstdout:{stdout}\nstderr:{stderr}", + stdout = String::from_utf8_lossy(&output.stdout), + stderr = String::from_utf8_lossy(&output.stderr) + ); + } +} + +/// Writes a `Cargo.toml` containing a workspace with `module_count` members to `w`. +/// +/// e.g. +/// ```toml +/// [workspace] +/// members = [ "mod_0", "mod_1" ] +/// ``` pub fn write_bin_cargo_toml( w: &mut impl std::io::Write, module_count: usize, @@ -47,6 +75,8 @@ pub fn write_bin_cargo_toml( writeln!(w, "]") } +/// Writes a `Cargo.toml` for a crate with name `name` to `w` that will contain a single Rust source +/// file with a subset of the testing being generated. pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> { write!( w, @@ -73,6 +103,8 @@ pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io: ) } +/// Writes a Rust source file into `w` with common definitions, static arrays with test values, +/// declarations of C wrapper functions for FFI and Rust test functions. pub fn write_lib_rs( w: &mut impl std::io::Write, notice: &str, @@ -106,7 +138,7 @@ pub fn write_lib_rs( let name = arg.rust_vals_array_name().to_string(); if seen.insert(name) { - ArgumentList::gen_arg_rust(arg, w, Indentation::default(), PASSES)?; + ArgumentList::gen_arg_rust(arg, w, PASSES)?; } } } @@ -121,6 +153,13 @@ pub fn write_lib_rs( Ok(()) } +/// Writes the body of an intrinsic test to `w` for `intrinsic`. +/// +/// Each specialisation of the intrinsic (i.e. specific instantiations of the immediate arguments +/// of the intrinsic) is added to an array of specialisations. Each specialisation is tested +/// (first loop) `PASSES` number of times (second loop). For a given iteration of a given +/// specialisation, test values are loaded for each argument and passed to the Rust intrinsic +/// and the C wrapper function, and the results are compared. fn generate_rust_test_loop( w: &mut impl std::io::Write, intrinsic: &Intrinsic, @@ -205,9 +244,7 @@ fn generate_rust_test_loop( " }}", " }}", ), - loaded_args = intrinsic - .arguments - .load_values_rust(Indentation::default().nest_by(4)), + loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), passes = passes, @@ -216,6 +253,8 @@ fn generate_rust_test_loop( ) } +/// Writes a test function for an given intrinsic to `w`, with a body generated by +/// `generate_rust_test_loop`. fn create_rust_test( w: &mut impl std::io::Write, intrinsic: &Intrinsic, @@ -235,6 +274,8 @@ fn create_rust_test( Ok(()) } +/// Writes an `extern "C"` block with function declarations for each of the C wrapper functions into +/// `w`. pub fn write_bindings_rust( w: &mut impl std::io::Write, i: usize, @@ -268,12 +309,24 @@ pub fn write_bindings_rust( writeln!(w, "}}") } +/// Writes a `build.rs` into `w` for each test crate that compiles the corresponding C source code +/// with wrapper functions. pub fn write_build_rs( w: &mut impl std::io::Write, i: usize, arch_flags: &[&str], + cli_options: &ProcessedCli, ) -> std::io::Result<()> { - const COMMON_FLAGS: &[&str] = &["-ffp-contract=off", "-ffp-model=strict", "-Wno-narrowing"]; + const COMMON_FLAGS: &[&str] = &["-ffp-contract=off", "-Wno-narrowing"]; + const CLANG_FLAGS: &[&str] = &["-ffp-model=strict"]; + const GCC_FLAGS: &[&str] = &[ + "-flax-vector-conversions", + "-fno-fast-math", + "-frounding-math", + "-fexcess-precision=standard", + "-ftrapping-math", + "-fsignaling-nans", + ]; write!( w, @@ -287,9 +340,17 @@ pub fn write_build_rs( i = i )?; - let indentation = Indentation::default().nest_by(2); - for flag in COMMON_FLAGS.iter().chain(arch_flags) { - writeln!(w, "{indentation}\"{flag}\",")?; + let compiler_specific_flags = match cli_options.cc_arg_style { + CcArgStyle::Gcc => GCC_FLAGS, + CcArgStyle::Clang => CLANG_FLAGS, + }; + + for flag in COMMON_FLAGS + .iter() + .chain(compiler_specific_flags) + .chain(arch_flags) + { + writeln!(w, "\"{flag}\",")?; } write!( diff --git a/library/stdarch/crates/intrinsic-test/src/common/indentation.rs b/library/stdarch/crates/intrinsic-test/src/common/indentation.rs deleted file mode 100644 index 9c2cc886e6544..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/common/indentation.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Basic code formatting tools. -//! -//! We don't need perfect formatting for the generated tests, but simple indentation can make -//! debugging a lot easier. - -#[derive(Copy, Clone, Debug, Default)] -pub struct Indentation(u32); - -impl Indentation { - pub fn nested(self) -> Self { - Self(self.0 + 1) - } - - pub fn nest_by(&self, additional_levels: u32) -> Self { - Self(self.0 + additional_levels) - } -} - -impl std::fmt::Display for Indentation { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - for _ in 0..self.0 { - write!(f, " ")?; - } - Ok(()) - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 76e5959153d07..d69644388a830 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -19,6 +19,10 @@ pub struct Intrinsic { pub arch_tags: Vec, } +/// Invokes `f` for each combination of the values in the constraint ranges. +/// +/// For example, given `constraints=[Equal(0), Range(1..2), Set([3, 4])]` and `imm_values=[]`, this +/// produces the four calls to `f`: `f([0, 1, 3])`, `f([0, 1, 4])`, `f([0, 2, 3])`, `f([0, 2, 4])`. fn recurse_specializations<'a, E>( constraints: &mut (impl Iterator + Clone), imm_values: &mut Vec, @@ -37,6 +41,13 @@ fn recurse_specializations<'a, E>( } impl Intrinsic { + /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the + /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds + /// to the value of the `i`th const generic argument of the intrinsic. + /// + /// For an intrinsic with three arguments with constraints `Equal(0)`, `Range(1..2)`, + /// `Set([3, 4])` respectively, this would produce four calls to `f`: `f(0, 1, 3)`, + /// `f(0, 1, 4)`, `f(0, 2, 3)`, `f(0, 2, 4)`. pub fn iter_specializations( &self, mut f: impl FnMut(&[i64]) -> Result<(), E>, diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index ab4a565200bc8..a894d5c0164e1 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -5,7 +5,6 @@ use std::str::FromStr; use itertools::Itertools as _; -use super::indentation::Indentation; use super::values::value_for_array; #[derive(Debug, PartialEq, Copy, Clone)] @@ -16,6 +15,7 @@ pub enum Sign { #[derive(Debug, PartialEq, Copy, Clone)] pub enum TypeKind { + Bool, BFloat, Float, Int(Sign), @@ -24,6 +24,8 @@ pub enum TypeKind { Void, Mask, Vector, + SvPattern, + SvPrefetchOp, } impl FromStr for TypeKind { @@ -31,17 +33,22 @@ impl FromStr for TypeKind { fn from_str(s: &str) -> Result { match s { - "bfloat" | "BF16" => Ok(Self::BFloat), - "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), - "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => { + "svbool" | "bool" => Ok(Self::Bool), + "svbfloat" | "bfloat" | "BF16" => Ok(Self::BFloat), + "svfloat" | "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), + "svint" | "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => { Ok(Self::Int(Sign::Signed)) } "poly" => Ok(Self::Poly), "char" => Ok(Self::Char(Sign::Signed)), - "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => Ok(Self::Int(Sign::Unsigned)), + "svuint" | "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => { + Ok(Self::Int(Sign::Unsigned)) + } "void" => Ok(Self::Void), "MASK" => Ok(Self::Mask), "M128" | "M256" | "M512" => Ok(Self::Vector), + "svpattern" => Ok(Self::SvPattern), + "svprfop" => Ok(Self::SvPrefetchOp), _ => Err(format!("Impossible to parse argument kind {s}")), } } @@ -53,6 +60,7 @@ impl fmt::Display for TypeKind { f, "{}", match self { + Self::Bool => "bool", Self::BFloat => "bfloat", Self::Float => "float", Self::Int(Sign::Signed) => "int", @@ -63,15 +71,18 @@ impl fmt::Display for TypeKind { Self::Char(Sign::Unsigned) => "unsigned char", Self::Mask => "mask", Self::Vector => "vector", + Self::SvPattern => "svpattern", + Self::SvPrefetchOp => "svprfop", } ) } } impl TypeKind { - /// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t. + /// Returns the type component of a C typedef for a type of the form of `{type}{size}_t` pub fn c_prefix(&self) -> &str { match self { + Self::Bool => "bool", Self::Float => "float", Self::Int(Sign::Signed) => "int", Self::Int(Sign::Unsigned) => "uint", @@ -83,7 +94,7 @@ impl TypeKind { } } - /// Gets the rust prefix for the type kind i.e. i, u, f. + /// Returns the Rust prefix for this type kind i.e. `i`, `u`, or `f`. pub fn rust_prefix(&self) -> &str { match self { Self::BFloat => "bf", @@ -99,37 +110,60 @@ impl TypeKind { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SimdLen { + Scalable, + Fixed(u32), +} + +impl std::fmt::Display for SimdLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Scalable => unimplemented!(), + Self::Fixed(len) => ::fmt(len, f), + } + } +} + #[derive(Debug, PartialEq, Clone)] pub struct IntrinsicType { + /// Is this an immediate? pub constant: bool, - /// whether this object is a const pointer + /// Is this is a const pointer to the type? pub ptr_constant: bool, + /// Is this is a pointer to the type? pub ptr: bool, + /// Element type (e.g. `TypeKind::Int(Sign::Unsigned)` for `uint64x2_t`). pub kind: TypeKind, - /// The bit length of this type (e.g. 32 for u32). + + /// Number of bits of this type (e.g. 32 for `u32`). pub bit_len: Option, - /// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None` - /// means this is not a simd type. A `None` can be assumed to be 1, - /// although in some places a distinction is needed between `u64` and - /// `uint64x1_t` this signals that. - pub simd_len: Option, + /// Length of a SIMD vector (i.e. `Fixed(4)` for `uint32x4_t`). + /// + /// A value of `None` means this is not a SIMD type. The number of lanes of a type with + /// `simd_len=None` can be assumed to be one, though it is important to maintain a distinction + /// between `simd_len=None` and `simd_len=Some(Fixed(1))` so as to differentiate between `u64` + /// and `uint64x1_t`. A value of `Some(Scalable)` indicates that this is a scalable vector. + pub simd_len: Option, - /// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t). - /// A value of `None` represents a type that does not contain any - /// rows encoded in the type (e.g. uint8x8_t). - /// A value of `None` can be assumed to be 1 though. + /// Number of rows of a SIMD matrix (i.e. 2 for `uint8x8x2_t`). + /// + /// A value of `None` means this is not a SIMD matrix (e.g. `uint8x8_t`). The number of rows of + /// a type with `vec_len=None` can be assumed to be one. pub vec_len: Option, } impl IntrinsicType { + /// Returns the element type pub fn kind(&self) -> TypeKind { self.kind } + /// Returns the number of bits of the type (with a minimum of `8`) pub fn inner_size(&self) -> u32 { if let Some(bl) = self.bit_len { cmp::max(bl, 8) @@ -138,37 +172,54 @@ impl IntrinsicType { } } + /// Returns the number of lanes of the type pub fn num_lanes(&self) -> u32 { - self.simd_len.unwrap_or(1) + self.simd_len + .as_ref() + .map(|len| match len { + SimdLen::Scalable => unimplemented!(), + SimdLen::Fixed(len) => *len, + }) + .unwrap_or(1) } + /// Returns the number of vectors of the type pub fn num_vectors(&self) -> u32 { self.vec_len.unwrap_or(1) } + /// Returns `true` if this represents a SIMD vector pub fn is_simd(&self) -> bool { self.simd_len.is_some() || self.vec_len.is_some() } + /// Returns `true` if this is a pointer pub fn is_ptr(&self) -> bool { self.ptr } - pub fn populate_random(&self, indentation: Indentation, loads: u32) -> String { + /// Returns the elements used in the test value arrays in `gen_arg_rust`. Uses the same + /// `num_lanes * num_vectors + loads - 1` arithmetic to produce the number of values that + /// `ArgumentList::gen_arg_rust` expects and `ArgumentList::load_values_rust` needs. + /// + /// Each value in the array starts as a bit pattern from `common::values::value_from_array` + /// which is then printed as a hex value in the generated code (and if identified as a negative + /// value, with the appropriate minus and corrected hex pattern). Calls to `fN::from_bits` are + /// generated for floats. + pub fn populate_random(&self, loads: u32) -> String { match self { IntrinsicType { bit_len: Some(bit_len @ (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16 | 32 | 64)), kind: kind @ (TypeKind::Int(_) | TypeKind::Poly | TypeKind::Char(_) | TypeKind::Mask), - simd_len, vec_len, .. } => { - let body_indentation = indentation.nested(); format!( - "[\n{body}\n{indentation}]", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| { + "[\n{body}\n]", + body = (0..(self.num_lanes() * vec_len.unwrap_or(1) + loads - 1)).format_with( + ",\n", + |i, fmt| { let src = value_for_array(*bit_len, i); assert!(src == 0 || src.ilog2() < *bit_len); if *kind == TypeKind::Int(Sign::Signed) && (src >> (*bit_len - 1)) != 0 @@ -177,43 +228,43 @@ impl IntrinsicType { let mask = !0u64 >> (64 - *bit_len); let ones_compl = src ^ mask; let twos_compl = ones_compl + 1; - fmt(&format_args!("{body_indentation}-{twos_compl:#x}")) + fmt(&format_args!("-{twos_compl:#x}")) } else { - fmt(&format_args!("{body_indentation}{src:#x}")) + fmt(&format_args!("{src:#x}")) } - }) + } + ) ) } IntrinsicType { kind: TypeKind::Float, bit_len: Some(bit_len @ (16 | 32 | 64)), - simd_len, vec_len, .. } => { format!( - "[\n{body}\n{indentation}]", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| fmt(&format_args!( - "{indentation}f{bit_len}::from_bits({src:#x})", - indentation = indentation.nested(), + "[\n{body}\n]", + body = (0..(self.num_lanes() * vec_len.unwrap_or(1) + loads - 1)).format_with( + ",\n", + |i, fmt| fmt(&format_args!( + "f{bit_len}::from_bits({src:#x})", src = value_for_array(*bit_len, i) - ))) + )) + ) ) } IntrinsicType { kind: TypeKind::Vector, bit_len: Some(128 | 256 | 512), - simd_len, vec_len, .. } => { - let body_indentation = indentation.nested(); let effective_bit_len = 32; format!( - "[\n{body}\n{indentation}]", - body = (0..(vec_len.unwrap_or(1) * simd_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| { + "[\n{body}\n]", + body = (0..(vec_len.unwrap_or(1) * self.num_lanes() + loads - 1)).format_with( + ",\n", + |i, fmt| { let src = value_for_array(effective_bit_len, i); assert!(src == 0 || src.ilog2() < effective_bit_len); if (src >> (effective_bit_len - 1)) != 0 { @@ -221,11 +272,12 @@ impl IntrinsicType { let mask = !0u64 >> (64 - effective_bit_len); let ones_compl = src ^ mask; let twos_compl = ones_compl + 1; - fmt(&format_args!("{body_indentation}-{twos_compl:#x}")) + fmt(&format_args!("-{twos_compl:#x}")) } else { - fmt(&format_args!("{body_indentation}{src:#x}")) + fmt(&format_args!("{src:#x}")) } - }) + } + ) ) } _ => unimplemented!("populate random: {self:#?}"), @@ -235,18 +287,16 @@ impl IntrinsicType { pub trait IntrinsicTypeDefinition: Deref { /// Determines the load function for this type. - /// can be implemented in an `impl` block fn get_load_function(&self) -> String; - /// Gets a string containing the typename for this type in C format. - /// can be directly defined in `impl` blocks + /// Gets a string containing the typename for this type in C. fn c_type(&self) -> String; - /// Gets a string containing the typename for this type in Rust format. - /// can be directly defined in `impl` blocks + /// Gets a string containing the typename for this type in Rust. fn rust_type(&self) -> String; - /// To enable architecture-specific logic + /// Gets a string containing the name of the scalar type corresponding to this type if it is a + /// vector. fn rust_scalar_type(&self) -> String { if self.is_simd() { format!( diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 86849f7db34e0..3775e453c24b0 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -6,7 +6,9 @@ use cli::ProcessedCli; use crate::common::{ gen_c::write_wrapper_c, - gen_rust::{write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs}, + gen_rust::{ + run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, + }, intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition, }; @@ -19,7 +21,6 @@ pub mod intrinsic_helpers; mod gen_c; mod gen_rust; -mod indentation; mod values; /// Architectures must support this trait @@ -29,7 +30,7 @@ pub trait SupportedArchitectureTest { fn intrinsics(&self) -> &[Intrinsic]; - fn create(cli_options: ProcessedCli) -> Self; + fn create(cli_options: &ProcessedCli) -> Self; const NOTICE: &str; @@ -38,14 +39,14 @@ pub trait SupportedArchitectureTest { const PLATFORM_RUST_CFGS: &str; const PLATFORM_RUST_DEFINITIONS: &str; - fn arch_flags(&self) -> Vec<&str>; + fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { - let (chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); + let (max_chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); std::fs::create_dir_all("c_programs").unwrap(); self.intrinsics() - .par_chunks(chunk_size) + .par_chunks(max_chunk_size) .enumerate() .map(|(i, chunk)| { let c_filename = format!("c_programs/wrapper_{i}.c"); @@ -56,25 +57,25 @@ pub trait SupportedArchitectureTest { .unwrap(); } - fn generate_rust_file(&self) { - let arch_flags = self.arch_flags(); + fn generate_rust_file(&self, cli_options: &ProcessedCli) { + let arch_flags = self.arch_flags(cli_options); std::fs::create_dir_all("rust_programs").unwrap(); - let (chunk_size, chunk_count) = manual_chunk(self.intrinsics().len()); + let (max_chunk_size, chunk_count) = manual_chunk(self.intrinsics().len()); let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); write_bin_cargo_toml(&mut cargo, chunk_count).unwrap(); self.intrinsics() - .chunks(chunk_size) + .chunks(max_chunk_size) .enumerate() .map(|(i, chunk)| { std::fs::create_dir_all(format!("rust_programs/mod_{i}/src"))?; let rust_filename = format!("rust_programs/mod_{i}/src/lib.rs"); trace!("generating `{rust_filename}`"); - let mut file = File::create(rust_filename)?; + let mut file = File::create(&rust_filename)?; write_lib_rs( &mut file, @@ -84,6 +85,7 @@ pub trait SupportedArchitectureTest { i, chunk, )?; + run_rustfmt(&rust_filename); let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); trace!("generating `{toml_filename}`"); @@ -93,9 +95,10 @@ pub trait SupportedArchitectureTest { let build_rs_filename = format!("rust_programs/mod_{i}/build.rs"); trace!("generating `{build_rs_filename}`"); - let mut file = File::create(build_rs_filename).unwrap(); + let mut file = File::create(&build_rs_filename).unwrap(); - write_build_rs(&mut file, i, &arch_flags).unwrap(); + write_build_rs(&mut file, i, &arch_flags, &cli_options).unwrap(); + run_rustfmt(&build_rs_filename); Ok(()) }) @@ -106,5 +109,7 @@ pub trait SupportedArchitectureTest { pub fn manual_chunk(intrinsic_count: usize) -> (usize, usize) { let ncores = std::thread::available_parallelism().unwrap().into(); - (intrinsic_count.div_ceil(ncores), ncores) + let max_intrinsics_per_chunk = intrinsic_count.div_ceil(ncores); + let number_of_chunks = intrinsic_count.div_ceil(max_intrinsics_per_chunk); + (max_intrinsics_per_chunk, number_of_chunks) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 6c94ef2c22e1d..01dc0713f0f00 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -1,6 +1,12 @@ -/// Get a single value for an argument values array in a determistic way. -/// * `bits`: The number of bits for the type, only 8, 16, 32, 64 are valid values -/// * `index`: The position in the array we are generating for +/// Returns a bit pattern for a value being output into a array of test values. Bit patterns come +/// from one of many constant arrays of test values. The specific constant array used depends on +/// the number of bits - `bits` - of the type having test values generated for it. This function +/// is called repeatedly with incrementing values of `index` to produce an entire array of test +/// values. +/// +/// Each constant array of bit patterns should ideally be at least the length of the largest array +/// of test values that will be requested (e.g. 51 for a `poly8x8x4` when `PASSES=20`: +/// `(8 * 4) + 20 - 1`), otherwise values will be repeated. pub fn value_for_array(bits: u32, index: u32) -> u64 { let index = index as usize; match bits { diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 9f57c99f12cf5..4c0136041fc35 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -18,18 +18,24 @@ fn main() { if processed_cli_options.target.starts_with("arm") | processed_cli_options.target.starts_with("aarch64") { - run(ArmArchitectureTest::create(processed_cli_options)) + run( + ArmArchitectureTest::create(&processed_cli_options), + processed_cli_options, + ) } else if processed_cli_options.target.starts_with("x86") { - run(X86ArchitectureTest::create(processed_cli_options)) + run( + X86ArchitectureTest::create(&processed_cli_options), + processed_cli_options, + ) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) } } -fn run(test_environment: impl SupportedArchitectureTest) { +fn run(test_environment: impl SupportedArchitectureTest, processed_cli_options: ProcessedCli) { info!("building C binaries"); test_environment.generate_c_file(); info!("building Rust binaries"); - test_environment.generate_rust_file(); + test_environment.generate_rust_file(&processed_cli_options); } diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index 5d4798482a1d3..288bd8bdf8961 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -29,8 +29,11 @@ impl SupportedArchitectureTest for X86ArchitectureTest { const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - fn arch_flags(&self) -> Vec<&str> { + fn arch_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ + "-maes", + "-mf16c", + "-mfma", "-mavx", "-mavx2", "-mavx512f", @@ -66,7 +69,7 @@ impl SupportedArchitectureTest for X86ArchitectureTest { ] } - fn create(cli_options: ProcessedCli) -> Self { + fn create(cli_options: &ProcessedCli) -> Self { let mut intrinsics = get_xml_intrinsics(&cli_options.filename).expect("Error parsing input file"); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/types.rs b/library/stdarch/crates/intrinsic-test/src/x86/types.rs index c6ea15e150752..a0e14c77d6b5e 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/types.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use itertools::Itertools; use super::intrinsic::X86IntrinsicType; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::{ + IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, +}; use crate::x86::xml_parser::Parameter; impl IntrinsicTypeDefinition for X86IntrinsicType { @@ -187,7 +189,7 @@ impl X86IntrinsicType { Ok(num_bits) => self .data .bit_len - .and_then(|bit_len| Some(num_bits / bit_len)), + .and_then(|bit_len| Some(SimdLen::Fixed(num_bits / bit_len))), Err(_) => None, }; } @@ -297,7 +299,7 @@ impl X86IntrinsicType { // - _mm512_conj_pch if param.type_data == "__m512h" && param.etype == "FP32" { data.bit_len = Some(16); - data.simd_len = Some(32); + data.simd_len = Some(SimdLen::Fixed(32)); } let mut result = X86IntrinsicType { diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs index bbe28174ffa05..934c7e28fe374 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs @@ -513,9 +513,11 @@ fn generate_functions(intrinsics: &[ScalarIntrinsic]) -> String { } // Attributes - output.push_str("#[inline(always)]\n"); if let Some(tf_attr) = info.arch_guard.target_feature_attr() { + output.push_str("#[inline]\n"); // https://github.com/rust-lang/rust/issues/145574 output.push_str(&format!("{}\n", tf_attr)); + } else { + output.push_str("#[inline(always)]\n"); } // Immediate parameters become const generics but are passed as positional @@ -662,7 +664,11 @@ fn main() -> Result<(), String> { let intrinsics = parse_header(&header_content); println!("Parsed {} scalar intrinsics", intrinsics.len()); - let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + let hexagon_dir = std::env::args() + .nth(1) + .map(std::path::PathBuf::from) + .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); + std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; let scalar_path = hexagon_dir.join("scalar.rs"); generate_scalar_file(&intrinsics, &scalar_path)?; diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index 8a58c66313daf..7a1c3030c0042 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -1691,7 +1691,11 @@ fn main() -> Result<(), String> { } // Generate output files - let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + let hexagon_dir = std::env::args() + .nth(1) + .map(std::path::PathBuf::from) + .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); + std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; // Generate v64.rs (64-byte vector mode) let v64_path = hexagon_dir.join("v64.rs"); diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec index 867e071b62edc..c74199c6a6e4c 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec @@ -1400,21 +1400,25 @@ asm-fmts = xd, xj, xk data-types = UV4DI, UV4DI, UV4DI /// lasx_xvrepl128vei_b +impl = portable name = lasx_xvrepl128vei_b asm-fmts = xd, xj, ui4 data-types = V32QI, V32QI, UQI /// lasx_xvrepl128vei_h +impl = portable name = lasx_xvrepl128vei_h asm-fmts = xd, xj, ui3 data-types = V16HI, V16HI, UQI /// lasx_xvrepl128vei_w +impl = portable name = lasx_xvrepl128vei_w asm-fmts = xd, xj, ui2 data-types = V8SI, V8SI, UQI /// lasx_xvrepl128vei_d +impl = portable name = lasx_xvrepl128vei_d asm-fmts = xd, xj, ui1 data-types = V4DI, V4DI, UQI @@ -1468,41 +1472,49 @@ asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI /// lasx_xvilvh_b +impl = portable name = lasx_xvilvh_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvilvh_h +impl = portable name = lasx_xvilvh_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvilvh_w +impl = portable name = lasx_xvilvh_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvilvh_d +impl = portable name = lasx_xvilvh_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI /// lasx_xvilvl_b +impl = portable name = lasx_xvilvl_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvilvl_h +impl = portable name = lasx_xvilvl_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvilvl_w +impl = portable name = lasx_xvilvl_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvilvl_d +impl = portable name = lasx_xvilvl_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI @@ -2685,26 +2697,31 @@ asm-fmts = xd, rj, ui2 data-types = V4DI, V4DI, DI, UQI /// lasx_xvreplve0_b +impl = portable name = lasx_xvreplve0_b asm-fmts = xd, xj data-types = V32QI, V32QI /// lasx_xvreplve0_h +impl = portable name = lasx_xvreplve0_h asm-fmts = xd, xj data-types = V16HI, V16HI /// lasx_xvreplve0_w +impl = portable name = lasx_xvreplve0_w asm-fmts = xd, xj data-types = V8SI, V8SI /// lasx_xvreplve0_d +impl = portable name = lasx_xvreplve0_d asm-fmts = xd, xj data-types = V4DI, V4DI /// lasx_xvreplve0_q +impl = portable name = lasx_xvreplve0_q asm-fmts = xd, xj data-types = V32QI, V32QI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec index b9df7bd96b9cb..c59f56a902d13 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec @@ -1420,21 +1420,25 @@ asm-fmts = vd, vj, rk data-types = V2DI, V2DI, SI /// lsx_vreplvei_b +impl = portable name = lsx_vreplvei_b asm-fmts = vd, vj, ui4 data-types = V16QI, V16QI, UQI /// lsx_vreplvei_h +impl = portable name = lsx_vreplvei_h asm-fmts = vd, vj, ui3 data-types = V8HI, V8HI, UQI /// lsx_vreplvei_w +impl = portable name = lsx_vreplvei_w asm-fmts = vd, vj, ui2 data-types = V4SI, V4SI, UQI /// lsx_vreplvei_d +impl = portable name = lsx_vreplvei_d asm-fmts = vd, vj, ui1 data-types = V2DI, V2DI, UQI @@ -1488,41 +1492,49 @@ asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI /// lsx_vilvh_b +impl = portable name = lsx_vilvh_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vilvh_h +impl = portable name = lsx_vilvh_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vilvh_w +impl = portable name = lsx_vilvh_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vilvh_d +impl = portable name = lsx_vilvh_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI /// lsx_vilvl_b +impl = portable name = lsx_vilvl_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vilvl_h +impl = portable name = lsx_vilvl_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vilvl_w +impl = portable name = lsx_vilvl_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vilvl_d +impl = portable name = lsx_vilvl_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt index e07ac41f9c44e..228c9140fc6aa 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt +++ b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt @@ -239,6 +239,18 @@ lsx_vpickod_b lsx_vpickod_h lsx_vpickod_w lsx_vpickod_d +lsx_vilvh_b +lsx_vilvh_h +lsx_vilvh_w +lsx_vilvh_d +lsx_vilvl_b +lsx_vilvl_h +lsx_vilvl_w +lsx_vilvl_d +lsx_vreplvei_b +lsx_vreplvei_h +lsx_vreplvei_w +lsx_vreplvei_d # LASX intrinsics lasx_xvsll_b @@ -475,3 +487,20 @@ lasx_xvpickod_b lasx_xvpickod_h lasx_xvpickod_w lasx_xvpickod_d +lasx_xvilvh_b +lasx_xvilvh_h +lasx_xvilvh_w +lasx_xvilvh_d +lasx_xvilvl_b +lasx_xvilvl_h +lasx_xvilvl_w +lasx_xvilvl_d +lasx_xvrepl128vei_b +lasx_xvrepl128vei_h +lasx_xvrepl128vei_w +lasx_xvrepl128vei_d +lasx_xvreplve0_b +lasx_xvreplve0_h +lasx_xvreplve0_w +lasx_xvreplve0_d +lasx_xvreplve0_q diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 360b8418d2194..4bd4fb0834e25 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2587,6 +2587,12 @@ pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection // statically. if builder.llvm_link_shared() { maybe_install_llvm(builder, target, &dst_libdir, false); + + // To workaround lack of rpath on Windows, we bundle another copy of + // the LLVM DLL to make rust-lld and llvm-tools work when `sysroot/bin` + // is missing from PATH, i.e. when they not launched by rustc. + let dst_libdir = sysroot.join("lib/rustlib").join(target).join("bin"); + maybe_install_llvm(builder, target, &dst_libdir, false); } } diff --git a/src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile new file mode 100644 index 0000000000000..ca597acb72c5c --- /dev/null +++ b/src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile @@ -0,0 +1,44 @@ +FROM ubuntu:26.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + clang \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + bzip2 \ + xz-utils \ + texinfo \ + wget \ + libssl-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/freebsd-toolchain.sh /tmp/ +RUN /tmp/freebsd-toolchain.sh aarch64 + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +COPY scripts/cmake.sh /scripts/ +RUN /scripts/cmake.sh + +ENV \ + AR_aarch64_unknown_freebsd=aarch64-unknown-freebsd14-ar \ + CC_aarch64_unknown_freebsd=aarch64-unknown-freebsd14-clang \ + CXX_aarch64_unknown_freebsd=aarch64-unknown-freebsd14-clang++ + +ENV HOSTS=aarch64-unknown-freebsd + +ENV RUST_CONFIGURE_ARGS="--enable-full-tools \ + --enable-extended \ + --enable-profiler \ + --enable-sanitizers \ + --disable-docs" + +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile new file mode 100644 index 0000000000000..57bc0a0d0abb5 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile @@ -0,0 +1,47 @@ +# This Dockerfile builds sysroot crates with the GCC backend and then run libcore tests +# with it. + +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + mingw-w64 \ + zlib1g-dev \ + libzstd-dev \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV NO_DEBUG_ASSERTIONS="1" +ENV RUSTFLAGS="-Cpanic=abort -Zpanic-abort-tests" +ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ + --enable-sanitizers \ + --enable-profiler \ + --enable-compiler-docs \ + --set llvm.libzstd=true \ + --set rust.codegen-backends=[\\\"gcc\\\"]" +ENV SCRIPT="python3 ../x.py \ + --stage 1 \ + test library/coretests" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile index 2208ed3ffbe15..dc7a680a095d3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile @@ -1,3 +1,6 @@ +# This Dockerfile builds sysroot with the LLVM backendsthen run tests with the +# GCC backend. + FROM ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh index 5308288d911a0..8b4081d27d083 100755 --- a/src/ci/docker/scripts/freebsd-toolchain.sh +++ b/src/ci/docker/scripts/freebsd-toolchain.sh @@ -46,6 +46,7 @@ mkdir -p "$sysroot" case $arch in (x86_64) freebsd_arch=amd64 ;; (i686) freebsd_arch=i386 ;; + (aarch64) freebsd_arch=arm64 ;; esac files_to_extract=( diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 97ddde799a757..6587a85ddcec1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -145,6 +145,9 @@ pr: - name: x86_64-gnu-gcc doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html <<: *job-linux-4c + - name: x86_64-gnu-gcc-core-tests + doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html + <<: *job-linux-4c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating @@ -261,6 +264,9 @@ auto: - name: dist-x86_64-freebsd <<: *job-linux-4c + - name: dist-aarch64-freebsd + <<: *job-aarch64-linux + - name: dist-x86_64-illumos <<: *job-linux-4c @@ -359,6 +365,10 @@ auto: doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html <<: *job-linux-4c + - name: x86_64-gnu-gcc-core-tests + doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html + <<: *job-linux-4c + # This job ensures commits landing on nightly still pass the full # test suite on the stable channel. There are some UI tests that # depend on the channel being built (for example if they include the diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 554bcfcf8e4b6..7e3da67d872c1 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -1018,12 +1018,13 @@ pub trait From: Sized { ### Formatting The string literals are format strings that accept parameters wrapped in braces -but positional and listed parameters and format specifiers are not accepted. +but positional and listed parameters are not accepted. The following parameter names are valid: - `Self` and all generic parameters of the trait. - `This`: the name of the trait the attribute is on, without generics. -- `Trait`: the name of the "sugared" trait. - See `TraitRefPrintSugared`. +- `This:path`: the full path of the trait the attribute is on, with unresolved generics. +- `This:resolved`: the full path of the trait the attribute is on, with resolved generics. +Additionally, this will "sugar" the `Fn(...)` traits. - `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`, `"a function"`, `"an async function"`, etc. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 7ffc9e3018471..36dc71a3ac0cc 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -346,17 +346,6 @@ See also the [codegen tests](#codegen-tests) for a similar set of tests. If you need to work with `#![no_std]` cross-compiling tests, consult the [`minicore` test auxiliary](./minicore.md) chapter. -#### Conditional assembly tests based on instruction support - -Tests that depend on specific assembly instructions being available can use the -`//@ needs-asm-mnemonic: ` directive. -This will skip the test if the target backend does not support the specified instruction mnemonic. - -For example, a test that requires the `RET` instruction: -```rust,ignore -//@ needs-asm-mnemonic: RET -``` - [`tests/assembly-llvm`]: https://github.com/rust-lang/rust/tree/HEAD/tests/assembly-llvm diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index d1b923edd3957..5dce2ad13a5fc 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -164,9 +164,10 @@ The following directives will check rustc build settings and target settings: For tests that cross-compile to explicit targets via `--target`, use `needs-llvm-components` instead to ensure the appropriate backend is available. -- `needs-asm-mnemonic: ` — ignores if the target backend does not - support the specified assembly mnemonic (e.g., `RET`, `NOP`). - Only supported with the LLVM backend. +- `needs-asm-ret` - ignores if the target does not have a `ret` instruction + in its assembly syntax. Most target architectures have this instruction, + making it handy for portable inline-assembly tests, but some architectures + (e.g. 32-bit ARM) do not have it. - `needs-profiler-runtime` — ignores the test if the profiler runtime was not enabled for the target (`build.profiler = true` in `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 088b453b55b17..74bb5efcabd85 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -183,6 +183,18 @@ affect that. The arguments to this flag are the same as those for the `-C` flag on rustc. Run `rustc -C help` to get the full list. +## `--remap-path-prefix`: remap source paths in output + +This flag is the equivalent flag from `rustc`: `--remap-path-prefix`. + +```bash +$ rustdoc src/lib.rs --remap-path-prefix="$PWD=/foo" +``` + +It permits remapping (as a best effort) source path prefixes in all output, including diagnostics, +debug information, macro expansions, generated documentation, etc. It takes a value of the +form `FROM=TO` where a path prefix equal to `FROM` is rewritten to the value `TO`. + ## `--test`: run code examples as tests Using this flag looks like this: diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 45f79f53f2c53..f16f375a5a84b 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -760,14 +760,6 @@ pass `--doctest-build-arg ARG` for each argument `ARG`. This flag enables the generation of toggles to expand macros in the HTML source code pages. -## `--remap-path-prefix`: Remap source code paths in output - -This flag is the equivalent flag from `rustc` `--remap-path-prefix`. - -it permits remapping source path prefixes in all output, including compiler diagnostics, -debug information, macro expansions, etc. It takes a value of the form `FROM=TO` -where a path prefix equal to `FROM` is rewritten to the value `TO`. - ## `--remap-path-scope`: Scopes to which the source remapping should be done This flag is the equivalent flag from `rustc` `--remap-path-scope`. diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md index 7fb33cd68b4a3..68b881df47385 100644 --- a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -37,3 +37,4 @@ which on their own don't do anything useful. In order to actually trace the functions, you will need to link a separate runtime library of your choice, such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). +On targets where such a runtime is not available but instrumentation is supported, you must supply and link your own runtime library. diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index bd27998b37706..f361fc38eeaba 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -169,7 +169,13 @@ def display_hint(): class StdVecDequeProvider(printer_base): def __init__(self, valobj): self._valobj = valobj - self._head = int(valobj["head"]) + + head = valobj["head"] + + # BACKCOMPAT: rust 1.95 + if head.type.code != gdb.TYPE_CODE_INT: + head = head[ZERO_FIELD] + self._head = int(head) self._size = int(valobj["len"]) # BACKCOMPAT: rust 1.75 cap = valobj["buf"]["inner"]["cap"] diff --git a/src/etc/lldb_batchmode.py b/src/etc/lldb_batchmode.py index 8728ddec8e6a6..54e796bb2bb79 100644 --- a/src/etc/lldb_batchmode.py +++ b/src/etc/lldb_batchmode.py @@ -97,10 +97,9 @@ def execute_command(command_interpreter, command): print_debug( "registering breakpoint callback, id = " + str(breakpoint_id) ) - callback_command = ( - "breakpoint command add -F breakpoint_callback " - + str(breakpoint_id) - ) + callback_command = f"breakpoint command add -s python {str(breakpoint_id)} -o \ + 'import lldb_batchmode; lldb_batchmode.breakpoint_callback'" + command_interpreter.HandleCommand(callback_command, res) if res.Succeeded(): print_debug( diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f20210323eb..2da6151607e3c 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -15,7 +15,7 @@ from rust_types import is_tuple_fields if TYPE_CHECKING: - from lldb import SBValue, SBType, SBTypeStaticField, SBTarget + from lldb import SBValue, SBType, SBTypeStaticField, SBTarget, SBProcess # from lldb.formatters import Logger @@ -289,6 +289,28 @@ def vec_to_string(vec: SBValue) -> str: ) +def read_string( + process: SBProcess, address: int, length: int, error: Optional[SBError] = None +) -> str: + """Reads a string from running process's memory. If `error` is passed in, it will be passed + to the `SBProcess.ReadMemory` call, and will reflect any errors after the function is called. + + If any error or exception occurs, a placeholder byte array of the form "" will + be returned instead.""" + + if error is None: + error = SBError() + try: + data = process.ReadMemory(address, length, error) + if error.Success(): + return '"' + data.decode("utf-8", "replace") + '"' + else: + return f"" + except Exception as e: + print(f"Unable to generate String summary: {e.__cause__}") + return "" + + def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): inner_vec = ( valobj.GetNonSyntheticValue() @@ -305,16 +327,25 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): ) length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) if length <= 0: return '""' - error = SBError() + + no_hi_bit_max: int = 1 << ((pointer.GetByteSize() * 8) - 1) + # technically length isn't a NoHighBit, but length should always be <= capacity + if length >= no_hi_bit_max or capacity >= no_hi_bit_max: + return "" + if pointer.GetValueAsUnsigned() == 0: + return "" + process = pointer.GetProcess() - data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) - if error.Success(): - return '"' + data.decode("utf8", "replace") + '"' - else: - raise Exception("ReadMemory error: %s", error.GetCString()) + + return read_string(process, pointer.GetValueAsAddress(), length) def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: @@ -363,15 +394,9 @@ def StdPathSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: data_ptr = valobj.GetChildMemberWithName("data_ptr") start = data_ptr.GetValueAsUnsigned() - error = SBError() process = data_ptr.GetProcess() - data = process.ReadMemory(start, length, error) - if PY3: - try: - data = data.decode(encoding="UTF-8") - except UnicodeDecodeError: - return "%r" % data - return '"%s"' % data + + return read_string(process, start, length) def sequence_formatter(output: str, valobj: SBValue, _dict: LLDBOpaque): @@ -443,6 +468,9 @@ def has_children(self) -> bool: class StdStringSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) + self.update() def update(self): @@ -454,7 +482,25 @@ def update(self): .GetChildMemberWithName("pointer") .GetChildMemberWithName("pointer") ) - self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + + self.capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) + + # As of 4/18/2026, LLDB cannot accurately determine the difference between Some("") and None + # this just makes sure we're not trying to access data when the string is clearly in an + # invalid state. + if ( + self.capacity >= self.no_hi_bit_max + or self.data_ptr.GetValueAsUnsigned() == 0 + ): + self.capacity = 0 + self.length = 0 + else: + self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.data_ptr.GetType().GetPointeeType() def has_children(self) -> bool: @@ -928,6 +974,8 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName()) self.valobj = valobj self.element_type = None + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) self.update() def num_children(self) -> int: @@ -949,15 +997,19 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: return element def update(self): - self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() - self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName( - "inner" - ) - + buf: SBValue = self.valobj.GetChildMemberWithName("buf") self.data_ptr = unwrap_unique_or_non_null( - self.buf.GetChildMemberWithName("ptr") + buf.GetChildMemberWithName("inner").GetChildMemberWithName("ptr") ) + capacity: int = buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + + if capacity >= self.no_hi_bit_max or self.data_ptr.GetValueAsUnsigned() == 0: + self.capacity = 0 + self.length = 0 + else: + self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) if not self.element_type.IsValid(): @@ -1032,7 +1084,7 @@ def StdSliceSummaryProvider(valobj, dict): class StdVecDequeSyntheticProvider: """Pretty-printer for alloc::collections::vec_deque::VecDeque - struct VecDeque { head: usize, len: usize, buf: RawVec } + struct VecDeque { head: WrappedIndex, len: usize, buf: RawVec } """ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): @@ -1061,7 +1113,11 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: return element def update(self): - self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() + head = self.valobj.GetChildMemberWithName("head") + # BACKCOMPAT: rust 1.95 + if head.GetType().num_fields == 1: + head = head.GetChildAtIndex(0) + self.head = head.GetValueAsUnsigned() self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName( "inner" diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index 1528a8b1226ca..dade85dafe738 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -23,7 +23,7 @@ - (($T1*)buf.inner.ptr.pointer.pointer)[(i + head) % buf.inner.cap.__0] + (($T1*)buf.inner.ptr.pointer.pointer)[(i + head.__0) % buf.inner.cap.__0] i = i + 1 diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2fe0c8d8776dd..2f992c622c496 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -20,7 +20,7 @@ use rustc_index::IndexVec; use rustc_metadata::rendered_const; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, TyCtxt, Visibility}; +use rustc_middle::ty::{self, Ty, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, }; @@ -1758,6 +1758,40 @@ impl PrimitiveType { } } + pub(crate) fn from_ty(ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Array(..) => Some(Self::Array), + ty::Bool => Some(Self::Bool), + ty::Char => Some(Self::Char), + ty::FnDef(..) | ty::FnPtr(..) => Some(Self::Fn), + ty::Int(int) => Some(Self::from(*int)), + ty::Uint(uint) => Some(Self::from(*uint)), + ty::Float(float) => Some(Self::from(*float)), + ty::Never => Some(Self::Never), + ty::Pat(..) => Some(Self::Pat), + ty::RawPtr(..) => Some(Self::RawPointer), + ty::Ref(..) => Some(Self::Reference), + ty::Slice(..) => Some(Self::Slice), + ty::Str => Some(Self::Str), + ty::Tuple(elems) if elems.is_empty() => Some(Self::Unit), + ty::Tuple(_) => Some(Self::Tuple), + ty::Adt(..) + | ty::Alias(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Error(..) + | ty::Foreign(..) + | ty::Infer(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::UnsafeBinder(..) => None, + } + } + pub(crate) fn simplified_types() -> &'static SimplifiedTypes { use PrimitiveType::*; use ty::{FloatTy, IntTy, UintTy}; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 35212d480cfdd..e6c64ef8b4220 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -9,8 +9,7 @@ use std::cmp::Ordering; use std::fmt::{self, Display, Write}; -use std::iter::{self, once}; -use std::slice; +use std::{iter, slice}; use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; @@ -434,27 +433,33 @@ fn generate_item_def_id_path( let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); + let mut prim = None; // No need to try to infer the actual parent item if it's not an associated item from the `impl` // block. if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - def_id = infcx + let ty = tcx.type_of(def_id); + let ty = infcx .at(&ObligationCause::dummy(), tcx.param_env(def_id)) - .query_normalize(ty::Binder::dummy( - tcx.type_of(def_id).instantiate_identity().skip_norm_wip(), - )) - .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) - .ok() - .and_then(|normalized| normalized.skip_binder().ty_adt_def()) - .map(|adt| adt.did()) - .unwrap_or(def_id); + .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip())) + .map(|resolved| infcx.resolve_vars_if_possible(resolved.value).skip_binder()) + .unwrap_or(ty.skip_binder()); + if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) { + def_id = new_def_id; + } else { + prim = PrimitiveType::from_ty(ty); + } } - let relative = clean::inline::item_relative_path(tcx, def_id); - let fqp: Vec = once(crate_name).chain(relative).collect(); - - let shortty = ItemType::from_def_id(def_id, tcx); + let mut fqp = vec![crate_name]; + let shortty = if let Some(prim) = prim { + fqp.push(prim.as_sym()); + ItemType::Primitive + } else { + fqp.append(&mut clean::inline::item_relative_path(tcx, def_id)); + ItemType::from_def_id(def_id, tcx) + }; let module_fqp = to_module_fqp(shortty, &fqp); let (parts, is_absolute) = url_parts(cx.cache(), def_id, module_fqp, &cx.current)?; diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1c162a79c4c44..7fcc6cd9efe4e 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -23,8 +23,8 @@ use crate::display::Joined as _; use crate::html::escape::EscapeBodyText; use crate::html::format::HrefInfo; use crate::html::macro_expansion::ExpandedCode; -use crate::html::render::span_map::{DUMMY_SP, Span}; -use crate::html::render::{Context, LinkFromSrc}; +use crate::html::render::Context; +use crate::html::span_map::{DUMMY_SP, LinkFromSrc, Span}; /// This type is needed in case we want to render links on items to allow to go to their definition. pub(crate) struct HrefContext<'a, 'tcx> { @@ -1001,8 +1001,8 @@ impl<'src> Classifier<'src> { has_ident = true; nb_items += 1; } else if nb > 0 && has_ident { - // Following `;` will be handled on its own. - break Some(nb_items - 1); + // Drop all the colons we just peeked (e.g. `Option::` → keep `Option`). + break Some(nb_items - nb); } else if has_ident { break Some(nb_items); } else { diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index d42f4782845d8..382d97265cf69 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod macro_expansion; pub mod markdown; pub(crate) mod render; pub(crate) mod sources; +pub(crate) mod span_map; pub(crate) mod static_files; pub(crate) mod toc; mod url_parts_builder; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index a2a43b3b2da75..9e52b03b3210b 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -20,7 +20,7 @@ use tracing::info; use super::print_item::{full_path, print_item, print_item_path, print_ty_path}; use super::sidebar::{ModuleLike, Sidebar, print_sidebar, sidebar_module_like}; -use super::{AllTypes, LinkFromSrc, StylePath, collect_spans_and_sources, scrape_examples_help}; +use super::{AllTypes, StylePath, scrape_examples_help}; use crate::clean::types::ExternalLocation; use crate::clean::utils::has_doc_flag; use crate::clean::{self, ExternalCrate}; @@ -33,8 +33,8 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::macro_expansion::ExpandedCode; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; -use crate::html::render::span_map::Span; use crate::html::render::write_shared::write_shared; +use crate::html::span_map::{LinkFromSrc, Span, collect_spans_and_sources}; use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; use crate::scrape_examples::AllCallLocations; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index fe7311719fd54..8108316a856ba 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -36,7 +36,6 @@ mod ordered_json; mod print_item; pub(crate) mod sidebar; mod sorted_template; -pub(crate) mod span_map; mod type_layout; mod write_shared; @@ -64,7 +63,6 @@ use rustc_span::symbol::{Symbol, sym}; use tracing::{debug, info}; pub(crate) use self::context::*; -pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; use crate::clean::{self, Defaultness, Item, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 45e38d67c4020..336513804e1ff 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -946,24 +946,22 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: let mut extern_crates = FxIndexSet::default(); - if !t.is_dyn_compatible(cx.tcx()) { - write!( - w, - "{}", - write_section_heading( - "Dyn Compatibility", - "dyn-compatibility", - None, - format!( - "

This trait is not \ - dyn compatible.

\ -

In older versions of Rust, dyn compatibility was called \"object safety\", \ - so this trait is not object safe.

", - base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION - ), + write!( + w, + "{}", + write_section_heading( + "Dyn Compatibility", + "dyn-compatibility", + None, + format!( + "

This trait {} \ + dyn compatible.

\ +

In older versions of Rust, dyn compatibility was called \"object safety\".

", + if t.is_dyn_compatible(cx.tcx()) { "is" } else { "is not" }, + base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION ), - )?; - } + ), + )?; if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) { // The DefId is for the first Type found with that name. The bool is diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/span_map.rs similarity index 100% rename from src/librustdoc/html/render/span_map.rs rename to src/librustdoc/html/span_map.rs diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 08777e8f40acc..24fa74cc84321 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -458,6 +458,14 @@ fn opts() -> Vec { By default, it is at `forbid` level.", "LEVEL", ), + opt( + Stable, + Multi, + "", + "remap-path-prefix", + "Remap source names in compiler messages", + "FROM=TO", + ), opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"), opt( Unstable, @@ -550,14 +558,6 @@ fn opts() -> Vec { "Force all doctests to be compiled as a single binary, instead of one binary per test. If merging fails, rustdoc will emit a hard error.", "yes|no|auto", ), - opt( - Unstable, - Multi, - "", - "remap-path-prefix", - "Remap source names in compiler messages", - "FROM=TO", - ), opt( Unstable, Opt, diff --git a/src/llvm-project b/src/llvm-project index eaab4d9841b9a..08c84e69a84d9 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit eaab4d9841b9a8a12783d927b2df2291c1c79269 +Subproject commit 08c84e69a84d95936296dfcab0e38b34100725d5 diff --git a/src/tools/cargo b/src/tools/cargo index 4d1f984518c77..fbb61be30e5f9 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 4d1f984518c77fad6eeef4f40153b002a659e662 +Subproject commit fbb61be30e5f9ac3a6ad58e56a5c0f5db2d2b3ef diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 63968a8b5e04e..29d0b47002679 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -92,26 +92,33 @@ impl LateLintPass<'_> for DbgMacro { (macro_call.span, String::from("()")) } }, - ExprKind::Match(args, _, _) => { - let suggestion = match args.kind { - // dbg!(1) => 1 - ExprKind::Tup([val]) => { - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string() + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. }, - // dbg!(2, 3) => (2, 3) - ExprKind::Tup([first, .., last]) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - format!("({snippet})") + .., + Expr { + kind: ExprKind::Match(last, ..), + .. }, - _ => unreachable!(), - }; - (macro_call.span, suggestion) + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) }, _ => unreachable!(), }; diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 7b408471574f9..c41ffad8431ba 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -726,6 +726,7 @@ pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { l.is_placeholder == r.is_placeholder && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) + && eq_mut_restriction(&l.mut_restriction, &r.mut_restriction) && both(l.ident.as_ref(), r.ident.as_ref(), |l, r| eq_id(*l, *r)) && eq_ty(&l.ty, &r.ty) } @@ -846,6 +847,10 @@ pub fn eq_impl_restriction(l: &ImplRestriction, r: &ImplRestriction) -> bool { eq_restriction_kind(&l.kind, &r.kind) } +pub fn eq_mut_restriction(l: &MutRestriction, r: &MutRestriction) -> bool { + eq_restriction_kind(&l.kind, &r.kind) +} + fn eq_restriction_kind(l: &RestrictionKind, r: &RestrictionKind) -> bool { match (l, r) { (RestrictionKind::Unrestricted, RestrictionKind::Unrestricted) => true, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 6a7637fb18585..492eb89708aeb 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -202,6 +202,7 @@ generate! { cx, cycle, cyclomatic_complexity, + dbg_macro, de, debug_struct, deprecated_in_future, diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 7600da856a18d..49b860d9cd4cf 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -16,7 +16,7 @@ use crate::directives::directive_names::{ pub(crate) use crate::directives::file::FileDirectives; use crate::directives::handlers::DIRECTIVE_HANDLERS_MAP; use crate::directives::line::DirectiveLine; -use crate::directives::needs::CachedNeedsConditions; +use crate::directives::needs::PreparedNeedsConditions; use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; use crate::executor::{CollectedTestDesc, ShouldFail}; @@ -40,14 +40,14 @@ pub(crate) struct DirectivesCache { /// "Conditions" used by `ignore-*` and `only-*` directives, prepared in /// advance so that they don't have to be evaluated repeatedly. cfg_conditions: cfg::PreparedConditions, - needs: CachedNeedsConditions, + needs: PreparedNeedsConditions, } impl DirectivesCache { pub(crate) fn load(config: &Config) -> Self { Self { cfg_conditions: cfg::prepare_conditions(config), - needs: CachedNeedsConditions::load(config), + needs: needs::prepare_needs_conditions(config), } } } diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index fb795c6b447c9..41ea492d6f38f 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -159,7 +159,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "min-llvm-version", "min-system-llvm-version", "minicore-compile-flags", - "needs-asm-mnemonic", + "needs-asm-ret", "needs-asm-support", "needs-backends", "needs-crate-type", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 88ca347edd0a5..ce67b11fba211 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -1,16 +1,154 @@ +use std::collections::HashMap; + use crate::common::{ Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer, query_rustc_output, }; -use crate::directives::{DirectiveLine, IgnoreDecision}; +use crate::directives::{DirectiveLine, IgnoreDecision, KNOWN_DIRECTIVE_NAMES_SET}; pub(super) fn handle_needs( - cache: &CachedNeedsConditions, + conditions: &PreparedNeedsConditions, config: &Config, ln: &DirectiveLine<'_>, ) -> IgnoreDecision { + let &DirectiveLine { name, .. } = ln; + + if !name.starts_with("needs-") { + return IgnoreDecision::Continue; + } + + if name == "needs-target-has-atomic" { + let Some(rest) = ln.value_after_colon() else { + return IgnoreDecision::Error { + message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(), + }; + }; + + // Expect directive value to be a list of comma-separated atomic widths. + let specified_widths = rest + .split(',') + .map(|width| width.trim()) + .map(ToString::to_string) + .collect::>(); + + for width in &specified_widths { + if !KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width.as_str()) { + return IgnoreDecision::Error { + message: format!( + "unknown width specified in `needs-target-has-atomic`: `{width}` is not a \ + known `target_has_atomic_width`, known values are `{:?}`", + KNOWN_TARGET_HAS_ATOMIC_WIDTHS + ), + }; + } + } + + let satisfies_all_specified_widths = specified_widths + .iter() + .all(|specified| config.target_cfg().target_has_atomic.contains(specified)); + if satisfies_all_specified_widths { + return IgnoreDecision::Continue; + } else { + return IgnoreDecision::Ignore { + reason: format!( + "skipping test as target does not support all of the required `target_has_atomic` widths `{:?}`", + specified_widths + ), + }; + } + } + + // FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above. + if name == "needs-crate-type" { + let Some(rest) = ln.value_after_colon() else { + return IgnoreDecision::Error { + message: + "expected `needs-crate-type` to have a comma-separated list of crate types" + .to_string(), + }; + }; + + // Expect directive value to be a list of comma-separated crate-types. + let specified_crate_types = rest + .split(',') + .map(|crate_type| crate_type.trim()) + .map(ToString::to_string) + .collect::>(); + + for crate_type in &specified_crate_types { + if !KNOWN_CRATE_TYPES.contains(&crate_type.as_str()) { + return IgnoreDecision::Error { + message: format!( + "unknown crate type specified in `needs-crate-type`: `{crate_type}` is not \ + a known crate type, known values are `{:?}`", + KNOWN_CRATE_TYPES + ), + }; + } + } + + let satisfies_all_crate_types = specified_crate_types + .iter() + .all(|specified| config.supported_crate_types().contains(specified)); + if satisfies_all_crate_types { + return IgnoreDecision::Continue; + } else { + return IgnoreDecision::Ignore { + reason: format!( + "skipping test as target does not support all of the crate types `{:?}`", + specified_crate_types + ), + }; + } + } + + // Handled elsewhere. + if name == "needs-llvm-components" || name == "needs-backends" { + return IgnoreDecision::Continue; + } + + if let Some(need) = conditions.simple_needs.get(name) { + if need.condition { + IgnoreDecision::Continue + } else { + IgnoreDecision::Ignore { + reason: if let Some(comment) = ln.remark_after_space() { + format!("{} ({})", need.ignore_reason, comment.trim()) + } else { + need.ignore_reason.into() + }, + } + } + } else { + IgnoreDecision::Error { message: format!("invalid needs directive: {name}") } + } +} + +struct Need { + name: &'static str, + condition: bool, + ignore_reason: &'static str, +} + +pub(crate) struct PreparedNeedsConditions { + /// The `//@ needs-*` conditions that can be treated as a simple name->boolean mapping. + simple_needs: HashMap<&'static str, Need>, +} + +pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditions { + let target = config.target.as_str(); + let sanitizers = config.target_cfg().sanitizers.as_slice(); + // Note that we intentionally still put the needs- prefix here to make the file show up when // grepping for a directive name, even though we could technically strip that. - let needs = &[ + let simple_needs = vec![ + // This used to be a more general `//@ needs-asm-mnemonic: ret` directive, + // but was simplified to just `//@ needs-asm-ret` because there are very + // few other mnemonics (`nop`?) that it could ever be useful with. + Need { + name: "needs-asm-ret", + condition: has_mnemonic(config, "ret"), + ignore_reason: "ignored on targets without a `ret` assembly instruction", + }, Need { name: "needs-asm-support", condition: config.has_asm_support(), @@ -18,77 +156,77 @@ pub(super) fn handle_needs( }, Need { name: "needs-sanitizer-support", - condition: cache.sanitizer_support, + condition: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), ignore_reason: "ignored on targets without sanitizers support", }, Need { name: "needs-sanitizer-address", - condition: cache.sanitizer_address, + condition: sanitizers.contains(&Sanitizer::Address), ignore_reason: "ignored on targets without address sanitizer", }, Need { name: "needs-sanitizer-cfi", - condition: cache.sanitizer_cfi, + condition: sanitizers.contains(&Sanitizer::Cfi), ignore_reason: "ignored on targets without CFI sanitizer", }, Need { name: "needs-sanitizer-dataflow", - condition: cache.sanitizer_dataflow, + condition: sanitizers.contains(&Sanitizer::Dataflow), ignore_reason: "ignored on targets without dataflow sanitizer", }, Need { name: "needs-sanitizer-kcfi", - condition: cache.sanitizer_kcfi, + condition: sanitizers.contains(&Sanitizer::Kcfi), ignore_reason: "ignored on targets without kernel CFI sanitizer", }, Need { name: "needs-sanitizer-kasan", - condition: cache.sanitizer_kasan, + condition: sanitizers.contains(&Sanitizer::KernelAddress), ignore_reason: "ignored on targets without kernel address sanitizer", }, Need { name: "needs-sanitizer-khwasan", - condition: cache.sanitizer_khwasan, + condition: sanitizers.contains(&Sanitizer::KernelHwaddress), ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", }, Need { name: "needs-sanitizer-leak", - condition: cache.sanitizer_leak, + condition: sanitizers.contains(&Sanitizer::Leak), ignore_reason: "ignored on targets without leak sanitizer", }, Need { name: "needs-sanitizer-memory", - condition: cache.sanitizer_memory, + condition: sanitizers.contains(&Sanitizer::Memory), ignore_reason: "ignored on targets without memory sanitizer", }, Need { name: "needs-sanitizer-thread", - condition: cache.sanitizer_thread, + condition: sanitizers.contains(&Sanitizer::Thread), ignore_reason: "ignored on targets without thread sanitizer", }, Need { name: "needs-sanitizer-hwaddress", - condition: cache.sanitizer_hwaddress, + condition: sanitizers.contains(&Sanitizer::Hwaddress), ignore_reason: "ignored on targets without hardware-assisted address sanitizer", }, Need { name: "needs-sanitizer-memtag", - condition: cache.sanitizer_memtag, + condition: sanitizers.contains(&Sanitizer::Memtag), ignore_reason: "ignored on targets without memory tagging sanitizer", }, Need { name: "needs-sanitizer-realtime", - condition: cache.sanitizer_realtime, + condition: sanitizers.contains(&Sanitizer::Realtime), ignore_reason: "ignored on targets without realtime sanitizer", }, Need { name: "needs-sanitizer-shadow-call-stack", - condition: cache.sanitizer_shadow_call_stack, + condition: sanitizers.contains(&Sanitizer::ShadowCallStack), ignore_reason: "ignored on targets without shadow call stacks", }, Need { name: "needs-sanitizer-safestack", - condition: cache.sanitizer_safestack, + condition: sanitizers.contains(&Sanitizer::Safestack), ignore_reason: "ignored on targets without SafeStack support", }, Need { @@ -133,17 +271,37 @@ pub(super) fn handle_needs( }, Need { name: "needs-xray", - condition: cache.xray, + condition: config.target_cfg().xray, ignore_reason: "ignored on targets without xray tracing", }, Need { name: "needs-rust-lld", - condition: cache.rust_lld, + condition: { + // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), + // we need to find whether `rust-lld` is present in the compiler under test. + // + // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For + // example: + // - on linux, it can be /lib + // - on windows, it can be /bin + // + // However, `rust-lld` is only located under the lib path, so we look for it there. + config + .host_compile_lib_path + .parent() + .expect("couldn't traverse to the parent of the specified --compile-lib-path") + .join("lib") + .join("rustlib") + .join(target) + .join("bin") + .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) + .exists() + }, ignore_reason: "ignored on targets without Rust's LLD", }, Need { name: "needs-dlltool", - condition: cache.dlltool, + condition: find_dlltool(config), ignore_reason: "ignored when dlltool for the current architecture is not present", }, Need { @@ -173,12 +331,12 @@ pub(super) fn handle_needs( }, Need { name: "needs-symlink", - condition: cache.symlinks, + condition: has_symlinks(), ignore_reason: "ignored if symlinks are unavailable", }, Need { name: "needs-llvm-zstd", - condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), + condition: config.default_codegen_backend.is_llvm() && llvm_has_zstd(config), ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", }, Need { @@ -202,242 +360,17 @@ pub(super) fn handle_needs( ignore_reason: "ignored if target does not support std", }, ]; - - let &DirectiveLine { name, .. } = ln; - - if name == "needs-target-has-atomic" { - let Some(rest) = ln.value_after_colon() else { - return IgnoreDecision::Error { - message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(), - }; - }; - - // Expect directive value to be a list of comma-separated atomic widths. - let specified_widths = rest - .split(',') - .map(|width| width.trim()) - .map(ToString::to_string) - .collect::>(); - - for width in &specified_widths { - if !KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width.as_str()) { - return IgnoreDecision::Error { - message: format!( - "unknown width specified in `needs-target-has-atomic`: `{width}` is not a \ - known `target_has_atomic_width`, known values are `{:?}`", - KNOWN_TARGET_HAS_ATOMIC_WIDTHS - ), - }; - } - } - - let satisfies_all_specified_widths = specified_widths - .iter() - .all(|specified| config.target_cfg().target_has_atomic.contains(specified)); - if satisfies_all_specified_widths { - return IgnoreDecision::Continue; - } else { - return IgnoreDecision::Ignore { - reason: format!( - "skipping test as target does not support all of the required `target_has_atomic` widths `{:?}`", - specified_widths - ), - }; - } - } - - // FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above. - if name == "needs-crate-type" { - let Some(rest) = ln.value_after_colon() else { - return IgnoreDecision::Error { - message: - "expected `needs-crate-type` to have a comma-separated list of crate types" - .to_string(), - }; - }; - - // Expect directive value to be a list of comma-separated crate-types. - let specified_crate_types = rest - .split(',') - .map(|crate_type| crate_type.trim()) - .map(ToString::to_string) - .collect::>(); - - for crate_type in &specified_crate_types { - if !KNOWN_CRATE_TYPES.contains(&crate_type.as_str()) { - return IgnoreDecision::Error { - message: format!( - "unknown crate type specified in `needs-crate-type`: `{crate_type}` is not \ - a known crate type, known values are `{:?}`", - KNOWN_CRATE_TYPES - ), - }; - } - } - - let satisfies_all_crate_types = specified_crate_types - .iter() - .all(|specified| config.supported_crate_types().contains(specified)); - if satisfies_all_crate_types { - return IgnoreDecision::Continue; - } else { - return IgnoreDecision::Ignore { - reason: format!( - "skipping test as target does not support all of the crate types `{:?}`", - specified_crate_types - ), - }; - } - } - - if name == "needs-asm-mnemonic" { - let Some(rest) = ln.value_after_colon() else { - return IgnoreDecision::Error { - message: "expected `needs-asm-mnemonic` to have a mnemonic name after colon" - .to_string(), - }; - }; - - if !config.default_codegen_backend.is_llvm() { - return IgnoreDecision::Ignore { - reason: "skipping test as non-LLVM backend does not support mnemonic queries" - .to_string(), - }; - } - - let mnemonic = rest.trim(); - let has_mnemonic = match mnemonic { - "ret" => cache.has_ret_mnemonic, - "nop" => cache.has_nop_mnemonic, - _ => has_mnemonic(config, mnemonic), - }; - - if has_mnemonic { - return IgnoreDecision::Continue; - } else { - return IgnoreDecision::Ignore { - reason: format!("skipping test as target does not have `{mnemonic}` mnemonic"), - }; - } - } - - if !name.starts_with("needs-") { - return IgnoreDecision::Continue; - } - - // Handled elsewhere. - if name == "needs-llvm-components" || name == "needs-backends" { - return IgnoreDecision::Continue; - } - - let mut found_valid = false; - for need in needs { - if need.name == name { - if need.condition { - found_valid = true; - break; - } else { - return IgnoreDecision::Ignore { - reason: if let Some(comment) = ln.remark_after_space() { - format!("{} ({})", need.ignore_reason, comment.trim()) - } else { - need.ignore_reason.into() - }, - }; - } - } - } - - if found_valid { - IgnoreDecision::Continue - } else { - IgnoreDecision::Error { message: format!("invalid needs directive: {name}") } - } -} - -struct Need { - name: &'static str, - condition: bool, - ignore_reason: &'static str, -} - -pub(super) struct CachedNeedsConditions { - sanitizer_support: bool, - sanitizer_address: bool, - sanitizer_cfi: bool, - sanitizer_dataflow: bool, - sanitizer_kcfi: bool, - sanitizer_kasan: bool, - sanitizer_khwasan: bool, - sanitizer_leak: bool, - sanitizer_memory: bool, - sanitizer_thread: bool, - sanitizer_hwaddress: bool, - sanitizer_memtag: bool, - sanitizer_realtime: bool, - sanitizer_shadow_call_stack: bool, - sanitizer_safestack: bool, - xray: bool, - rust_lld: bool, - dlltool: bool, - symlinks: bool, - /// Whether LLVM built with zstd, for the `needs-llvm-zstd` directive. - llvm_zstd: bool, - /// Might add particular other mnemonics heavily needed by tests here. - /// Otherwise call into llvm for every check - has_ret_mnemonic: bool, - has_nop_mnemonic: bool, -} - -impl CachedNeedsConditions { - pub(super) fn load(config: &Config) -> Self { - let target = &&*config.target; - let sanitizers = &config.target_cfg().sanitizers; - Self { - sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), - sanitizer_address: sanitizers.contains(&Sanitizer::Address), - sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi), - sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow), - sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi), - sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress), - sanitizer_khwasan: sanitizers.contains(&Sanitizer::KernelHwaddress), - sanitizer_leak: sanitizers.contains(&Sanitizer::Leak), - sanitizer_memory: sanitizers.contains(&Sanitizer::Memory), - sanitizer_thread: sanitizers.contains(&Sanitizer::Thread), - sanitizer_hwaddress: sanitizers.contains(&Sanitizer::Hwaddress), - sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag), - sanitizer_realtime: sanitizers.contains(&Sanitizer::Realtime), - sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack), - sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack), - xray: config.target_cfg().xray, - - // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), - // we need to find whether `rust-lld` is present in the compiler under test. - // - // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For - // example: - // - on linux, it can be /lib - // - on windows, it can be /bin - // - // However, `rust-lld` is only located under the lib path, so we look for it there. - rust_lld: config - .host_compile_lib_path - .parent() - .expect("couldn't traverse to the parent of the specified --compile-lib-path") - .join("lib") - .join("rustlib") - .join(target) - .join("bin") - .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) - .exists(), - - llvm_zstd: llvm_has_zstd(&config), - dlltool: find_dlltool(&config), - symlinks: has_symlinks(), - has_ret_mnemonic: has_mnemonic(config, "ret"), - has_nop_mnemonic: has_mnemonic(config, "nop"), - } - } + let simple_needs = simple_needs + .into_iter() + .map(|need| { + let name = need.name; + assert!(name.starts_with("needs-"), "must start with `needs-`: {name:?}"); + assert!(KNOWN_DIRECTIVE_NAMES_SET.contains(name), "unknown directive name: {name:?}"); + (name, need) + }) + .collect::>(); + + PreparedNeedsConditions { simple_needs } } fn find_dlltool(config: &Config) -> bool { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index bc016cfb1cb43..65d7b5360ae0d 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1270,23 +1270,15 @@ fn test_edition_range_edition_to_test() { } #[test] -fn needs_asm_mnemonic() { +fn needs_asm_ret() { let config_x86_64 = cfg().target("x86_64-unknown-linux-gnu").build(); let config_aarch64 = cfg().target("aarch64-unknown-linux-gnu").build(); - - // invalid mnemonic - assert!(check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:GRUGGY")); - assert!(check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:gruggy")); - - // valid x86 and aarch64 - assert!(!check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:RET")); - assert!(!check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:ret")); - - // this is aarch64 specific - assert!(check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:ldrsbwui")); - assert!(!check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:LDRSBWui")); - - // this is x86 specific - assert!(check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:CMPxCHG16B")); - assert!(!check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:CMPXchg16B")); + // 32-bit ARM does not have a "ret" mnemonic. + let config_arm32 = cfg().target("armv7a-none-eabi").build(); + let config_wasm = cfg().target("wasm32v1-none").build(); + + assert!(!check_ignore(&config_x86_64, "//@ needs-asm-ret")); + assert!(!check_ignore(&config_aarch64, "//@ needs-asm-ret")); + assert!(check_ignore(&config_arm32, "//@ needs-asm-ret")); + assert!(check_ignore(&config_wasm, "//@ needs-asm-ret")); } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index a2bf6c0839df4..72817ad64521a 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -745,6 +745,11 @@ impl<'test> TestCx<'test> { } } + unexpected.sort_by_key(|e| (e.line_num, e.column_num)); + unimportant.sort_by_key(|e| (e.line_num, e.column_num)); + + // `not_found` are sorted because `expected_errors` are sorted as they are read from file + // line by line. let mut not_found = Vec::new(); // anything not yet found is a problem for (index, expected_error) in expected_errors.iter().enumerate() { diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index d469502c243f4..24fe5b66bb0b4 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -114,8 +114,8 @@ impl Command { Command::Check { features, flags } => Self::check(features, flags), Command::Test { bless, target, coverage, features, flags } => Self::test(bless, target, coverage, features, flags), - Command::Run { dep, quiet, target, edition, features, flags } => - Self::run(dep, quiet, target, edition, features, flags), + Command::Run { dep, native, quiet, target, edition, features, flags } => + Self::run(dep, native, quiet, target, edition, features, flags), Command::Doc { features, flags } => Self::doc(features, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { features, flags } => Self::clippy(features, flags), @@ -465,6 +465,7 @@ impl Command { fn run( dep: bool, + native: bool, quiet: bool, target: Option, edition: Option, @@ -472,8 +473,10 @@ impl Command { flags: Vec, ) -> Result<()> { let mut e = MiriEnv::new()?; + let run_via_ui_test = dep || native; // Preparation: get a sysroot, and get the miri binary. + // We do this even for native run as it also builds Miri itself. let miri_sysroot = e.build_miri_sysroot(/* quiet */ quiet, target.as_deref(), &features)?; let miri_bin = e @@ -484,8 +487,8 @@ impl Command { // (because `flags` may contain `--`). let mut early_flags = Vec::::new(); - // In `dep` mode, the target is already passed via `MIRI_TEST_TARGET` - if !dep { + // In ui_test mode, the target is already passed via `MIRI_TEST_TARGET` + if !run_via_ui_test { if let Some(target) = &target { early_flags.push("--target".into()); early_flags.push(target.into()); @@ -493,35 +496,36 @@ impl Command { } early_flags.push("--edition".into()); early_flags.push(edition.as_deref().unwrap_or("2021").into()); - early_flags.push("--sysroot".into()); - early_flags.push(miri_sysroot.into()); + if !native { + early_flags.push("--sysroot".into()); + early_flags.push(miri_sysroot.into()); + } // Compute flags. let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default(); let miri_flags = flagsplit(&miri_flags); - let quiet_flag = if quiet { Some("--quiet") } else { None }; // Run Miri. // The basic command that executes the Miri driver. - let mut cmd = if dep { + let mut cmd = if run_via_ui_test { // We invoke the test suite as that has all the logic for running with dependencies. - e.cargo_cmd(".", "test", &features) + let mut cmd = e + .cargo_cmd(".", "test", &features) .args(&["--test", "ui"]) - .args(quiet_flag) + // This does not show anything useful so we always hide it. + .arg("--quiet") .arg("--") - .args(&["--miri-run-dep-mode"]) + .env("MIRI_RUN_MODE", if native { "native" } else { "1" }); + if let Some(target) = &target { + cmd = cmd.env("MIRI_TEST_TARGET", target); + } + cmd } else { cmd!(e.sh, "{miri_bin}") }; cmd.set_quiet(quiet); // Add Miri flags - let mut cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags); - // For `--dep` we also need to set the target in the env var. - if dep { - if let Some(target) = &target { - cmd = cmd.env("MIRI_TEST_TARGET", target); - } - } + let cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags); // Finally, run the thing. Ok(cmd.run()?) } diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index 419c128e5b72f..b575842fc6c78 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -78,6 +78,10 @@ pub enum Command { /// Build the program with the dependencies declared in `tests/deps/Cargo.toml`. #[arg(long)] dep: bool, + /// Compile and run the program natively instead of via Miri. Implies `--dep`. + /// All flags are passed to rustc; there is currently no way to pass flags to the program. + #[arg(long)] + native: bool, /// Hide build progress. #[arg(long, short)] quiet: bool, diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4bb9513424511..5e581cb38e5e6 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -63b1dfc0e00fd6f8ad7cd8817fc712e7d9b7be59 +281c97c3240a9abd984ca0c6a2cd7389115e80d5 diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index 16e89ee460de0..9d4c446b14f85 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -456,7 +456,7 @@ struct DisplayFmtWrapper { /// will show each permission line as /// ```text /// 0.. 1.. 2.. 3.. 4.. 5 -/// [Act|Res|Frz|Dis|___] +/// [Unq|Res|Frz|Dis|___] /// ``` struct DisplayFmtPermission { /// Text that starts the permission block. @@ -467,7 +467,7 @@ struct DisplayFmtPermission { close: S, /// Text to show when a permission is not initialized. /// Should have the same width as a `Permission`'s `.short_name()`, i.e. - /// 3 if using the `Res/Act/Frz/Dis` notation. + /// 3 if using the `Res/Unq/Frz/Dis` notation. uninit: S, /// Text to separate the `start` and `end` values of a range. range_sep: S, @@ -525,7 +525,7 @@ struct DisplayFmtPadding { /// ``` /// will show states as /// ```text -/// Act +/// Unq /// ?Res /// ____ /// ``` @@ -549,8 +549,8 @@ struct DisplayFmt { } impl DisplayFmt { /// Print the permission with the format - /// ` Res`/` Re*`/` Act`/` Frz`/` Dis` for accessed locations - /// and `?Res`/`?Re*`/`?Act`/`?Frz`/`?Dis` for unaccessed locations. + /// ` Res`/` Re*`/` Unq`/` Frz`/` Dis` for accessed locations + /// and `?Res`/`?Re*`/`?Unq`/`?Frz`/`?Dis` for unaccessed locations. fn print_perm(&self, perm: Option) -> String { if let Some(perm) = perm { format!( @@ -801,7 +801,7 @@ impl DisplayRepr { ) { let mut line = String::new(); // Format the permissions on each range. - // Looks like `| Act| Res| Res| Act|`. + // Looks like `| Unq| Res| Res| Unq|`. line.push_str(fmt.perm.open); for (i, (perm, &pad)) in tree.rperm.iter().zip(padding.iter()).enumerate() { if i > 0 { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index aa15b9e1b1188..0dd4712b85c7d 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -462,7 +462,7 @@ pub mod diagnostics { ReservedFrz { conflicted: false } => "Res ", ReservedFrz { conflicted: true } => "ResC", ReservedIM => "ReIM", - Unique => "Act ", + Unique => "Unq ", Frozen => "Frz ", Disabled => "Dis ", } diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index 47608f873a481..6556975095528 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -1,5 +1,7 @@ use std::cell::Cell; -use std::time::{Duration, Instant as StdInstant}; +use std::time::{Duration, Instant as StdInstant, SystemTime}; + +use crate::MiriMachine; /// When using a virtual clock, this defines how many nanoseconds we pretend are passing for each /// basic block. @@ -8,6 +10,7 @@ use std::time::{Duration, Instant as StdInstant}; /// (See `tests/pass/shims/time-with-isolation*.rs`.) const NANOSECONDS_PER_BASIC_BLOCK: u128 = 5000; +/// An instant (a fixed moment in time) in Miri's monotone clock. #[derive(Debug)] pub struct Instant { kind: InstantKind, @@ -129,3 +132,79 @@ impl MonotonicClock { } } } + +/// A deadline for some event to occur. +#[derive(Debug)] +pub enum Deadline { + Monotonic(Instant), + RealTime(SystemTime), +} + +impl From for Deadline { + fn from(value: Instant) -> Self { + Deadline::Monotonic(value) + } +} + +impl Deadline { + /// Will try to add `duration`, but if that overflows it may add less. + fn add_lossy(&self, duration: Duration) -> Self { + match self { + Deadline::Monotonic(i) => Deadline::Monotonic(i.add_lossy(duration)), + Deadline::RealTime(s) => { + // If this overflows, try adding just 1h and assume that will not overflow. + Deadline::RealTime( + s.checked_add(duration) + .unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()), + ) + } + } + } +} + +/// The clock to use for the timeout you are asking for. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum TimeoutClock { + /// The timeout is measured using the monotone clock. + Monotonic, + /// The timeout is measured using the host's system clock. + RealTime, +} + +/// Whether the timeout is relative or absolute. +#[derive(Debug, Copy, Clone)] +pub enum TimeoutStyle { + /// The given duration is interpreted relative to "now" for the selected clock. + Relative, + /// The given duration is interpreted as an "absolute" time, i.e., relative to the epoch of the + /// selected clock. + Absolute, +} + +impl MiriMachine<'_> { + /// Computes the deadline for a given timeout configuration and duration. + pub fn timeout( + &self, + clock: TimeoutClock, + style: TimeoutStyle, + duration: Duration, + ) -> Deadline { + // First let's figure out what "zero" means for the given clock and style. + let zero = match clock { + TimeoutClock::RealTime => { + assert!(self.communicate(), "cannot have `RealTime` timeout with isolation"); + Deadline::RealTime(match style { + TimeoutStyle::Absolute => SystemTime::UNIX_EPOCH, + TimeoutStyle::Relative => SystemTime::now(), + }) + } + TimeoutClock::Monotonic => + Deadline::Monotonic(match style { + TimeoutStyle::Absolute => self.monotonic_clock.epoch(), + TimeoutStyle::Relative => self.monotonic_clock.now(), + }), + }; + // Then add the given duration relative to that "zero". + zero.add_lossy(duration) + } +} diff --git a/src/tools/miri/src/concurrency/blocking_io.rs b/src/tools/miri/src/concurrency/blocking_io.rs index 9dc9bbfd1f1d2..c16aecedb3340 100644 --- a/src/tools/miri/src/concurrency/blocking_io.rs +++ b/src/tools/miri/src/concurrency/blocking_io.rs @@ -355,7 +355,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { &mut self, source_fd: FileDescriptionRef, interest: BlockingIoInterest, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -370,7 +370,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } else { // The I/O readiness is currently not fulfilled. We block the thread // until the readiness is fulfilled and execute the callback then. - this.block_thread(BlockReason::IO, timeout, callback); + this.block_thread(BlockReason::IO, deadline, callback); interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index c529ed5145edd..de2e0d8e23924 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -5,7 +5,6 @@ use std::collections::hash_map::Entry; use std::default::Default; use std::ops::Not; use std::rc::Rc; -use std::time::Duration; use std::{fmt, iter}; use rustc_abi::Size; @@ -684,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, condvar_ref: CondvarRef, mutex_ref: MutexRef, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, retval_succ: Scalar, retval_timeout: Scalar, dest: MPlaceTy<'tcx>, @@ -706,7 +705,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condvar_ref.0.borrow_mut().waiters.push_back(thread); this.block_thread( BlockReason::Condvar, - timeout, + deadline, callback!( @capture<'tcx> { condvar_ref: CondvarRef, @@ -763,7 +762,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, futex_ref: FutexRef, bitset: u32, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); @@ -776,7 +775,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.block_thread( BlockReason::Futex, - timeout, + deadline, callback!( @capture<'tcx> { futex_ref: FutexRef, diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 3d4210276ceeb..2b293a59ddb70 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -125,7 +125,7 @@ enum ThreadState<'tcx> { /// The thread is enabled and can be executed. Enabled, /// The thread is blocked on something. - Blocked { reason: BlockReason, timeout: Option, callback: DynUnblockCallback<'tcx> }, + Blocked { reason: BlockReason, deadline: Option, callback: DynUnblockCallback<'tcx> }, /// The thread has terminated its execution. We do not delete terminated /// threads (FIXME: why?). Terminated, @@ -135,8 +135,11 @@ impl<'tcx> std::fmt::Debug for ThreadState<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Enabled => write!(f, "Enabled"), - Self::Blocked { reason, timeout, .. } => - f.debug_struct("Blocked").field("reason", reason).field("timeout", timeout).finish(), + Self::Blocked { reason, deadline, .. } => + f.debug_struct("Blocked") + .field("reason", reason) + .field("deadline", deadline) + .finish(), Self::Terminated => write!(f, "Terminated"), } } @@ -375,52 +378,6 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { } } -/// The moment in time when a blocked thread should be woken up. -#[derive(Debug)] -enum Timeout { - Monotonic(Instant), - RealTime(SystemTime), -} - -impl Timeout { - /// How long do we have to wait from now until the specified time? - fn get_wait_time(&self, clock: &MonotonicClock) -> Duration { - match self { - Timeout::Monotonic(instant) => instant.duration_since(clock.now()), - Timeout::RealTime(time) => - time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO), - } - } - - /// Will try to add `duration`, but if that overflows it may add less. - fn add_lossy(&self, duration: Duration) -> Self { - match self { - Timeout::Monotonic(i) => Timeout::Monotonic(i.add_lossy(duration)), - Timeout::RealTime(s) => { - // If this overflows, try adding just 1h and assume that will not overflow. - Timeout::RealTime( - s.checked_add(duration) - .unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()), - ) - } - } - } -} - -/// The clock to use for the timeout you are asking for. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum TimeoutClock { - Monotonic, - RealTime, -} - -/// Whether the timeout is relative or absolute. -#[derive(Debug, Copy, Clone)] -pub enum TimeoutAnchor { - Relative, - Absolute, -} - /// An error signaling that the requested thread doesn't exist or has terminated. #[derive(Debug, Copy, Clone)] pub enum ThreadLookupError { @@ -655,12 +612,12 @@ impl<'tcx> ThreadManager<'tcx> { fn block_thread( &mut self, reason: BlockReason, - timeout: Option, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) { let state = &mut self.threads[self.active_thread].state; assert!(state.is_enabled()); - *state = ThreadState::Blocked { reason, timeout, callback } + *state = ThreadState::Blocked { reason, deadline, callback } } /// Change the active thread to some enabled thread. @@ -755,7 +712,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by // abstime has already been passed at the time of the call". // - let potential_sleep_time = this.unblock_expired_timeouts()?; + let potential_sleep_time = this.unblock_expired_deadlines()?; let thread_manager = &mut this.machine.threads; let rng = this.machine.rng.get_mut(); @@ -856,19 +813,27 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// Find all threads with expired timeouts, unblock them and execute their timeout callbacks. /// - /// This method returns the minimum duration until the next thread timeout expires. - /// If all ready threads have no timeout set, [`None`] is returned. - fn unblock_expired_timeouts(&mut self) -> InterpResult<'tcx, Option> { + /// This method returns the minimum duration until the next thread deadline. + /// If all ready threads have no deadline set, [`None`] is returned. + fn unblock_expired_deadlines(&mut self) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - let clock = &this.machine.monotonic_clock; + let communicate = this.machine.communicate(); let mut min_wait_time = Option::::None; let mut callbacks = Vec::new(); for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() { match &thread.state { - ThreadState::Blocked { timeout: Some(timeout), .. } => { - let wait_time = timeout.get_wait_time(clock); + ThreadState::Blocked { deadline: Some(deadline), .. } => { + let wait_time = match deadline { + Deadline::Monotonic(instant) => + instant.duration_since(this.machine.monotonic_clock.now()), + Deadline::RealTime(time) => { + assert!(communicate, "cannot have `RealTime` timeout with isolation"); + time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO) + } + }; + if wait_time.is_zero() { // The timeout expired for this thread. let old_state = mem::replace(&mut thread.state, ThreadState::Enabled); @@ -1098,34 +1063,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn block_thread( &mut self, reason: BlockReason, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); - if timeout.is_some() && this.machine.data_race.as_genmc_ref().is_some() { + if deadline.is_some() && this.machine.data_race.as_genmc_ref().is_some() { panic!("Unimplemented: Timeouts not yet supported in GenMC mode."); } - let timeout = timeout.map(|(clock, anchor, duration)| { - let anchor = match clock { - TimeoutClock::RealTime => { - assert!( - this.machine.communicate(), - "cannot have `RealTime` timeout with isolation enabled!" - ); - Timeout::RealTime(match anchor { - TimeoutAnchor::Absolute => SystemTime::UNIX_EPOCH, - TimeoutAnchor::Relative => SystemTime::now(), - }) - } - TimeoutClock::Monotonic => - Timeout::Monotonic(match anchor { - TimeoutAnchor::Absolute => this.machine.monotonic_clock.epoch(), - TimeoutAnchor::Relative => this.machine.monotonic_clock.now(), - }), - }; - anchor.add_lossy(duration) - }); - this.machine.threads.block_thread(reason, timeout, callback); + if matches!(deadline, Some(Deadline::RealTime(_))) && !this.machine.communicate() { + panic!("cannot have `RealTime` timeout with isolation"); + } + this.machine.threads.block_thread(reason, deadline, callback); } /// Put the blocked thread into the enabled state. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 944ff933ac9ba..160f31d50c4bf 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -133,7 +133,7 @@ pub use crate::borrow_tracker::tree_borrows::{EvalContextExt as _, Tree}; pub use crate::borrow_tracker::{ BorTag, BorrowTrackerMethod, EvalContextExt as _, TreeBorrowsParams, }; -pub use crate::clock::{Instant, MonotonicClock}; +pub use crate::clock::{Deadline, Instant, MonotonicClock, TimeoutClock, TimeoutStyle}; pub use crate::concurrency::blocking_io::{ BlockingIoInterest, BlockingIoManager, BlockingIoSourceReadiness, EvalContextExt as _, SourceFileDescription, @@ -146,7 +146,7 @@ pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceRef}; pub use crate::concurrency::sync::{CondvarRef, EvalContextExt as _, MutexRef, RwLockRef}; pub use crate::concurrency::thread::{ BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId, - ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind, + ThreadManager, UnblockKind, }; pub use crate::concurrency::{GenmcConfig, GenmcCtx, run_genmc_mode}; pub use crate::data_structures::dedup_range_map::DedupRangeMap; diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs index 02353411eb944..3656a9eaa87c2 100644 --- a/src/tools/miri/src/provenance_gc.rs +++ b/src/tools/miri/src/provenance_gc.rs @@ -44,6 +44,12 @@ where } } +impl VisitProvenance for Vec { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + self.iter().for_each(|el| el.visit_provenance(visit)); + } +} + impl VisitProvenance for std::cell::RefCell { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { self.borrow().visit_provenance(visit) diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index c66082583b3e4..27571a9eb7c87 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -381,7 +381,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut fields = vec![]; for field in &adt_def.non_enum_variant().fields { - let layout = this.layout_of(field.ty(*this.tcx, args).skip_norm_wip()).map_err(|_err| orig_ty)?; + let layout = this + .layout_of(field.ty(*this.tcx, args).skip_norm_wip()) + .map_err(|_err| orig_ty)?; fields.push(this.ty_to_ffitype(layout)?); } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 66bf50e5f18d2..87dded0766ad8 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -334,11 +334,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let deadline = this.read_scalar(deadline_op)?.to_u64()?; // Our mach_absolute_time "ticks" are plain nanoseconds. - let duration = Duration::from_nanos(deadline); + let deadline = Duration::from_nanos(deadline); + // This is *absolute* time. + let deadline = this.machine.monotonic_clock.epoch().add_lossy(deadline); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, duration)), + Some(deadline.into()), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { @@ -362,10 +364,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(duration) = this.read_timespec(&duration)? else { return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; + let deadline = this.machine.monotonic_clock.now().add_lossy(duration); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), + Some(deadline.into()), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { @@ -401,14 +404,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; - let timeout_anchor = if flags == 0 { + let timeout_style = if flags == 0 { // No flags set, the timespec should be interpreted as a duration - // to sleep for - TimeoutAnchor::Relative + // to sleep for, i.e., a relative time. + TimeoutStyle::Relative } else if flags == this.eval_libc_i32("TIMER_ABSTIME") { // Only flag TIMER_ABSTIME set, the timespec should be interpreted as // an absolute time. - TimeoutAnchor::Absolute + TimeoutStyle::Absolute } else { // The standard lib (through `sleep_until`) only needs TIMER_ABSTIME throw_unsup_format!( @@ -416,10 +419,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { TIMER_ABSTIME is supported" ); }; + let deadline = this.machine.timeout(TimeoutClock::Monotonic, timeout_style, duration); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, timeout_anchor, duration)), + Some(deadline), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { @@ -440,10 +444,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timeout_ms = this.read_scalar(timeout)?.to_u32()?; let duration = Duration::from_millis(timeout_ms.into()); + let deadline = this.machine.monotonic_clock.now().add_lossy(duration); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), + Some(deadline.into()), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 4a1dc10341646..ab17be0e04f4e 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -5,10 +5,10 @@ use std::io; use std::io::ErrorKind; use rand::RngExt; -use rustc_abi::Size; +use rustc_abi::{Align, Size}; use rustc_target::spec::Os; -use crate::shims::files::FileDescription; +use crate::shims::files::{DynFileDescriptionRef, FileDescription}; use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::linux_like::epoll::EpollReadiness; use crate::shims::unix::*; @@ -316,7 +316,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .min(u64::try_from(this.target_isize_max()).unwrap()) .min(u64::try_from(isize::MAX).unwrap()); let count = usize::try_from(count).unwrap(); // now it fits in a `usize` - let communicate = this.machine.communicate(); // Get the FD. let Some(fd) = this.machine.fds.get(fd_num) else { @@ -324,33 +323,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; - // Handle the zero-sized case. The man page says: - // > If count is zero, read() may detect the errors described below. In the absence of any - // > errors, or if read() does not check for errors, a read() with a count of 0 returns zero - // > and has no other effects. - if count == 0 { - this.write_null(dest)?; - return interp_ok(()); - } - // Non-deterministically decide to further reduce the count, simulating a partial read (but - // never to 0, that would indicate EOF). - let count = if this.machine.short_fd_operations - && fd.short_fd_operations() - && count >= 2 - && this.machine.rng.get_mut().random() - { - count / 2 // since `count` is at least 2, the result is still at least 1 - } else { - count - }; - trace!("read: FD mapped to {fd:?}"); // We want to read at most `count` bytes. We are sure that `count` is not negative // because it was a target's `usize`. Also we are sure that it's smaller than // `usize::MAX` because it is bounded by the host's `isize`. - let finish = { - let dest = dest.clone(); + let dest = dest.clone(); + this.read_from_fd( + fd, + buf, + count, + offset, callback!( @capture<'tcx> { count: usize, @@ -363,22 +346,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This must fit since `count` fits. this.write_int(u64::try_from(read_size).unwrap(), &dest) } - Err(e) => { - this.set_last_error_and_return(e, &dest) - } + Err(e) => this.set_last_error_and_return(e, &dest) }} - ) - }; - match offset { - None => fd.read(communicate, buf, count, this, finish)?, - Some(offset) => { - let Ok(offset) = u64::try_from(offset) else { - return this.set_last_error_and_return(LibcError("EINVAL"), dest); - }; - fd.as_unix(this).pread(communicate, offset, buf, count, this, finish)? - } - }; - interp_ok(()) + ), + ) } fn write( @@ -402,64 +373,379 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .min(u64::try_from(this.target_isize_max()).unwrap()) .min(u64::try_from(isize::MAX).unwrap()); let count = usize::try_from(count).unwrap(); // now it fits in a `usize` - let communicate = this.machine.communicate(); // We temporarily dup the FD to be able to retain mutable access to `this`. let Some(fd) = this.machine.fds.get(fd_num) else { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + let dest = dest.clone(); + this.write_to_fd( + fd, + buf, + count, + offset, + callback!( + @capture<'tcx> { + count: usize, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + match result { + Ok(write_size) => { + assert!(write_size <= count); + // This must fit since `count` fits. + this.write_int(u64::try_from(write_size).unwrap(), &dest) + } + Err(e) => this.set_last_error_and_return(e, &dest) + + }} + ), + ) + } + + /// Vectored reads are implemented by first reading bytes from `fd` + /// into a temporary buffer which has the combined size of all buffers in + /// `iov`. After that we split the bytes of the combined buffer into the + /// buffers of `iov`. This ensures that the vectored read occurs atomically. + fn readv( + &mut self, + fd: &OpTy<'tcx>, + iov: &OpTy<'tcx>, + iovcnt: &OpTy<'tcx>, + offset: Option<&OpTy<'tcx>>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let fd = this.read_scalar(fd)?.to_i32()?; + let iov_ptr = this.read_pointer(iov)?; + let iovcnt: u64 = this.read_scalar(iovcnt)?.to_i32()?.try_into().unwrap(); + // `readv` is the same as `preadv` without an offset. + let offset = if let Some(offset) = offset { + if matches!(this.tcx.sess.target.os, Os::Solaris) { + throw_unsup_format!( + "preadv: vectored reads with offsets aren't supported on Solaris" + ) + } + Some(this.read_scalar(offset)?.to_int(offset.layout.size)?) + } else { + None + }; + + // Check that the FD exists. + let Some(fd) = this.machine.fds.get(fd) else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; + + let iovec_layout = this.libc_array_ty_layout("iovec", iovcnt); + let iov_ptr_mplace = this.ptr_to_mplace(iov_ptr, iovec_layout); + + // Read list of buffers from `iov`. + let mut buffers = Vec::new(); + + let mut array = this.project_array_fields(&iov_ptr_mplace)?; + while let Some((_idx, iovec)) = array.next(this)? { + let iov_len_field = this.project_field_named(&iovec, "iov_len")?; + let iov_len: u64 = this + .read_scalar(&iov_len_field)? + .to_int(iov_len_field.layout.size)? + .try_into() + .unwrap(); + + let iov_base_field = this.project_field_named(&iovec, "iov_base")?; + let iov_base_ptr = this.read_pointer(&iov_base_field)?; + + buffers.push((iov_base_ptr, iov_len)); + } + + let total_bytes = buffers.iter().map(|(_, len)| len).sum::(); + + // Allocate a temporary buffer which has the combined size of all buffers provided in `iov`. + let tmp_ptr: Pointer = this + .allocate_ptr( + Size::from_bytes(total_bytes), + Align::ONE, + MemoryKind::Stack, + AllocInit::Uninit, + )? + .into(); + + let dest = dest.clone(); + this.read_from_fd( + fd, + tmp_ptr, + usize::try_from(total_bytes).unwrap(), + offset, + callback!( + @capture<'tcx> { + tmp_ptr: Pointer, + buffers: Vec<(Pointer, u64)>, + dest: MPlaceTy<'tcx> + } |this, result: Result| { + let bytes_read = match result { + Ok(size) => { + this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest)?; + u64::try_from(size).unwrap() + }, + Err(e) => return this.set_last_error_and_return(e, &dest) + }; + let mut remaining_bytes = bytes_read; + + // Split the bytes from the temporary buffer into the buffers provided in `iov`. + // We start at the first buffer and fill them in order, until we reach the end of the + // initialized bytes in the temporary buffer. + for (buffer_ptr, buffer_len) in buffers { + // Offset temporary buffer by the amount of bytes we already copied into previous buffers. + let tmp_ptr_with_offset = + this.ptr_offset_inbounds(tmp_ptr, i64::try_from(bytes_read.strict_sub(remaining_bytes)).unwrap())?; + + // Copy at most as many bytes as the buffer fits but without reading + // any uninitialized bytes from the temporary buffer. + let copy_amount = buffer_len.min(remaining_bytes); + this.mem_copy( + tmp_ptr_with_offset, + buffer_ptr, + Size::from_bytes(copy_amount), + // The buffers are guaranteed to not overlap because we just newly allocated + // the `tmp_ptr`, and `tmp_ptr_with_offset` is guaranteed to be + // within those boundaries. + true, + )?; + + remaining_bytes = remaining_bytes.strict_sub(copy_amount); + if remaining_bytes == 0 { + // We don't have anything left to copy; exit the loop. + break; + } + } + + this.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack) + }), + ) + } + + /// Vectored writes are implemented by first writing the bytes from all + /// buffers of `iov` into a combined temporary buffer and then writing this + /// combined buffer into `fd`. This ensures that the vectored write occurs atomically. + fn writev( + &mut self, + fd: &OpTy<'tcx>, + iov: &OpTy<'tcx>, + iovcnt: &OpTy<'tcx>, + offset: Option<&OpTy<'tcx>>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let fd = this.read_scalar(fd)?.to_i32()?; + let iov_ptr = this.read_pointer(iov)?; + let iovcnt: u64 = this.read_scalar(iovcnt)?.to_i32()?.try_into().unwrap(); + // `writev` is the same as `pwritev` without an offset. + let offset = if let Some(offset) = offset { + if matches!(this.tcx.sess.target.os, Os::Solaris) { + throw_unsup_format!( + "pwritev: vectored writes with offsets aren't supported on Solaris" + ) + } + Some(this.read_scalar(offset)?.to_int(offset.layout.size)?) + } else { + None + }; + + // Check that the FD exists. + let Some(fd) = this.machine.fds.get(fd) else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; + + let iovec_layout = this.libc_array_ty_layout("iovec", iovcnt); + let iov_ptr_mplace = this.ptr_to_mplace(iov_ptr, iovec_layout); + + // Read list of buffers from `iov`. + let mut buffers = Vec::new(); + + let mut array = this.project_array_fields(&iov_ptr_mplace)?; + while let Some((_idx, iovec)) = array.next(this)? { + let iov_len_field = this.project_field_named(&iovec, "iov_len")?; + let iov_len: u64 = this + .read_scalar(&iov_len_field)? + .to_int(iov_len_field.layout.size)? + .try_into() + .unwrap(); + + let iov_base_field = this.project_field_named(&iovec, "iov_base")?; + let iov_base_ptr = this.read_pointer(&iov_base_field)?; + + buffers.push((iov_base_ptr, iov_len)); + } + + let total_bytes = buffers.iter().map(|(_, len)| len).sum::(); + + // Allocate a temporary buffer which has the combined size of all buffers provided in `iov`. + let tmp_ptr: Pointer = this + .allocate_ptr( + Size::from_bytes(total_bytes), + Align::ONE, + MemoryKind::Stack, + AllocInit::Uninit, + )? + .into(); + + // Copy the bytes from all buffers provided in `iov` into the temporary buffer. + // We start at the first buffer and then continue buffer by buffer. + let mut bytes_copied: u64 = 0; + for (buffer_ptr, buffer_len) in buffers { + // Offset temporary buffer by the amount of bytes we already copied from previous buffers. + let tmp_ptr_with_offset = + this.ptr_offset_inbounds(tmp_ptr, i64::try_from(bytes_copied).unwrap())?; + + this.mem_copy( + buffer_ptr, + tmp_ptr_with_offset, + Size::from_bytes(buffer_len), + // The buffers are guaranteed to not overlap because we just newly allocated + // the `tmp_ptr`, and `tmp_ptr_with_offset` is guaranteed to be + // within those boundaries. + true, + )?; + + bytes_copied = bytes_copied.strict_add(buffer_len); + } + + let dest = dest.clone(); + // Write bytes from the temporary buffer. This ensures the write is atomic. + this.write_to_fd( + fd, + tmp_ptr, + usize::try_from(total_bytes).unwrap(), + offset, + callback!( + @capture<'tcx> { + tmp_ptr: Pointer, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + this.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack)?; + match result { + Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest), + Err(e) => this.set_last_error_and_return(e, &dest) + } + }), + ) + } +} + +impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Read `len` bytes from the `fd` file description at `offset` into the buffer + /// pointed to by `ptr`. + /// If `offset` is [`Some`], the read occurs at the given absolute position rather + /// than the current file position (`read_at` semantics rather than `read`). + /// `finish` will be invoked when the read is done (which might be way after + /// this function returns as the read may block). + fn read_from_fd( + &mut self, + fd: DynFileDescriptionRef, + ptr: Pointer, + len: usize, + offset: Option, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + // Handle the zero-sized case. The man page says: + // > If count is zero, read() may detect the errors described below. In the absence of any + // > errors, or if read() does not check for errors, a read() with a count of 0 returns zero + // > and has no other effects. + if len == 0 { + return finish.call(this, Ok(0)); + } + + // Non-deterministically decide to further reduce the length, simulating a partial read (but + // never to 0, that would indicate EOF). + let len = if this.machine.short_fd_operations + && fd.short_fd_operations() + && len >= 2 + && this.machine.rng.get_mut().random() + { + len / 2 // since `len` is at least 2, the result is still at least 1 + } else { + len + }; + + match offset { + None => fd.read(this.machine.communicate(), ptr, len, this, finish)?, + Some(offset) => { + let Ok(offset) = u64::try_from(offset) else { + return finish.call(this, Err(LibcError("EINVAL"))); + }; + fd.as_unix(this).pread( + this.machine.communicate(), + offset, + ptr, + len, + this, + finish, + )? + } + }; + interp_ok(()) + } + + /// Write `len` bytes at `offset` from the buffer pointed to by `ptr` into the `fd` + /// file description. + /// If `offset` is [`Some`], the write occurs at the given absolute position rather + /// than the current file position (`write_at` semantics rather than `write`). + /// `finish` will be invoked when the write is done (which might be way after + /// this function returns as the write may block). + fn write_to_fd( + &mut self, + fd: DynFileDescriptionRef, + ptr: Pointer, + len: usize, + offset: Option, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + // Handle the zero-sized case. The man page says: // > If count is zero and fd refers to a regular file, then write() may return a failure // > status if one of the errors below is detected. If no errors are detected, or error // > detection is not performed, 0 is returned without causing any other effect. If count // > is zero and fd refers to a file other than a regular file, the results are not // > specified. - if count == 0 { + if len == 0 { // For now let's not open the can of worms of what exactly "not specified" could mean... - this.write_null(dest)?; - return interp_ok(()); + return finish.call(this, Ok(0)); } - // Non-deterministically decide to further reduce the count, simulating a partial write. + + // Non-deterministically decide to further reduce the length, simulating a partial write. // We avoid reducing the write size to 0: the docs seem to be entirely fine with that, // but the standard library is not (https://github.com/rust-lang/rust/issues/145959). - let count = if this.machine.short_fd_operations + let len = if this.machine.short_fd_operations && fd.short_fd_operations() - && count >= 2 + && len >= 2 && this.machine.rng.get_mut().random() { - count / 2 + len / 2 } else { - count + len }; - let finish = { - let dest = dest.clone(); - callback!( - @capture<'tcx> { - count: usize, - dest: MPlaceTy<'tcx>, - } - |this, result: Result| { - match result { - Ok(write_size) => { - assert!(write_size <= count); - // This must fit since `count` fits. - this.write_int(u64::try_from(write_size).unwrap(), &dest) - } - Err(e) => { - this.set_last_error_and_return(e, &dest) - } - }} - ) - }; match offset { - None => fd.write(communicate, buf, count, this, finish)?, + None => fd.write(this.machine.communicate(), ptr, len, this, finish)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - return this.set_last_error_and_return(LibcError("EINVAL"), dest); + return finish.call(this, Err(LibcError("EINVAL"))); }; - fd.as_unix(this).pwrite(communicate, buf, count, offset, this, finish)? + fd.as_unix(this).pwrite( + this.machine.communicate(), + ptr, + len, + offset, + this, + finish, + )? } }; interp_ok(()) diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 2ed708975c7d5..67602a4fe88e2 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -226,8 +226,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); this.write(fd, buf, count, None, dest)?; } + "readv" => { + let [fd, iov, iovcnt] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32) -> isize), + link_name, + abi, + args, + )?; + this.readv(fd, iov, iovcnt, None, dest)?; + } + "writev" => { + let [fd, iov, iovcnt] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32) -> isize), + link_name, + abi, + args, + )?; + this.writev(fd, iov, iovcnt, None, dest)?; + } "pread" => { - // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize), link_name, @@ -241,7 +258,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, Some(offset), dest)?; } "pwrite" => { - // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize), link_name, @@ -255,6 +271,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } + "preadv" => { + let [fd, iov, iovcnt, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32, libc::off_t) -> isize), + link_name, + abi, + args, + )?; + this.readv(fd, iov, iovcnt, Some(offset), dest)?; + } + "pwritev" => { + let [fd, iov, iovcnt, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32, libc::off_t) -> isize), + link_name, + abi, + args, + )?; + this.writev(fd, iov, iovcnt, Some(offset), dest)?; + } + "close" => { let [fd] = this.check_shim_sig( shim_sig!(extern "C" fn(i32) -> i32), diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index 7c46dd549bc0a..32338391f2dd8 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -93,7 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // extended variant, `struct _umtx_time`, as the `uaddr2` argument of _umtx_op(). // They are distinguished by the `uaddr` value, which must be equal // to the size of the structure pointed to by `uaddr2`, casted to uintptr_t. - let timeout = if this.ptr_is_null(uaddr2)? { + let deadline = if this.ptr_is_null(uaddr2)? { // no timeout parameter None } else { @@ -105,13 +105,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; - let anchor = if umtx_time.abs_time { - TimeoutAnchor::Absolute + let style = if umtx_time.abs_time { + TimeoutStyle::Absolute } else { - TimeoutAnchor::Relative + TimeoutStyle::Relative }; - Some((umtx_time.timeout_clock, anchor, umtx_time.timeout)) + Some(this.machine.timeout( + umtx_time.timeout_clock, + style, + umtx_time.timeout, + )) } else if uaddr == timespec_layout.size.bytes() { // RealTime clock can't be used in isolation mode. this.check_no_isolation("`_umtx_op` with `timespec` timeout")?; @@ -127,7 +131,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // code (umtx_copyin_umtx_time() in kern_umtx.c), it seems to default to CLOCK_REALTIME, // so that's what we also do. // Discussion in golang: https://github.com/golang/go/issues/17168#issuecomment-250235271 - Some((TimeoutClock::RealTime, TimeoutAnchor::Relative, duration)) + Some(this.machine.timeout( + TimeoutClock::RealTime, + TimeoutStyle::Relative, + duration, + )) } else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); } @@ -137,7 +145,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.futex_wait( futex_ref, u32::MAX, // we set the bitset to include all bits - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx>, diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0caae04d93644..a5b3f891d9ca7 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::ffi::OsString; use std::fs::{self, DirBuilder, File, FileType, OpenOptions, TryLockError}; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; -use std::path::{self, Path, PathBuf}; +use std::path::{self, Path}; use std::time::SystemTime; use rustc_abi::Size; @@ -750,7 +750,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // However, `statx` is allowed to return information that was not requested or to not // return information that was requested. This `mask` represents the information we can // actually provide for any target. - let mut mask = this.eval_libc_u32("STATX_TYPE") | this.eval_libc_u32("STATX_SIZE"); + let mut mask = this.eval_libc_u32("STATX_TYPE") + | this.eval_libc_u32("STATX_MODE") + | this.eval_libc_u32("STATX_SIZE"); // Check which pieces of metadata we acquired, and set the appropriate flags in the mask. if metadata.ino.is_some() { @@ -1650,13 +1652,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Write the modified template back to the passed in pointer to maintain POSIX semantics. this.write_bytes_ptr(template_ptr, template_bytes.iter().copied())?; - // To actually open the file, turn this into a host OsString. - let p = bytes_to_os_str(template_bytes)?.to_os_string(); - - let possibly_unique = std::env::temp_dir().join::(p.into()); - - let file = fopts.open(possibly_unique); - + // See if we can create and open this file. + let file = fopts.open(bytes_to_os_str(template_bytes)?); match file { Ok(f) => { let fd = this.machine.fds.insert_new(FileHandle { file: f, writable: true }); diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index 1758c04c8981e..d2d978bffee7d 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -473,11 +473,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If the timeout is 0 or there is a ready event, we can return immediately. return_ready_list(&epfd, dest, &event, this)?; } else { - // Blocking - let timeout = match timeout { + // Blocking, with a relative timeout. + let deadline = match timeout { 0.. => { let duration = Duration::from_millis(timeout.try_into().unwrap()); - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)) + Some(this.machine.monotonic_clock.now().add_lossy(duration).into()) } -1 => None, ..-1 => { @@ -495,7 +495,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // a thread is permanently blocked so this is fine. this.block_thread( BlockReason::Epoll { epfd: epfd.clone() }, - timeout, + deadline, callback!( @capture<'tcx> { epfd: FileDescriptionRef, diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 76374ca24d0da..ee318a265fd30 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -97,8 +97,9 @@ impl FileDescription for EventFd { ) -> InterpResult<'tcx> { // We're treating the buffer as a `u64`. let ty = ecx.machine.layouts.u64; - // Check the size of slice, and return error only if the size of the slice < 8. - if len < ty.layout.size.bytes_usize() { + // Check the size of slice, and return error if the size is wrong. The docs say we only + // error when the size is too small, but Linux seems to also error when the size is too big. + if len != ty.layout.size.bytes_usize() { return finish.call(ecx, Err(ErrorKind::InvalidInput.into())); } diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 00df4ee2b2ae3..5a1a9ed535941 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -68,7 +68,7 @@ pub fn futex<'tcx>( } let timeout = ecx.deref_pointer_as(timeout, ecx.libc_ty_layout("timespec"))?; - let timeout = if ecx.ptr_is_null(timeout.ptr())? { + let deadline = if ecx.ptr_is_null(timeout.ptr())? { None } else { let Some(duration) = ecx.read_timespec(&timeout)? else { @@ -82,14 +82,14 @@ pub fn futex<'tcx>( } else { TimeoutClock::Monotonic }; - let timeout_anchor = if wait_bitset { + let timeout_style = if wait_bitset { // FUTEX_WAIT_BITSET uses an absolute timestamp. - TimeoutAnchor::Absolute + TimeoutStyle::Absolute } else { // FUTEX_WAIT uses a relative timestamp. - TimeoutAnchor::Relative + TimeoutStyle::Relative }; - Some((timeout_clock, timeout_anchor, duration)) + Some(ecx.machine.timeout(timeout_clock, timeout_style, duration)) }; // There may be a concurrent thread changing the value of addr // and then invoking the FUTEX_WAKE syscall. It is critical that the @@ -154,7 +154,7 @@ pub fn futex<'tcx>( ecx.futex_wait( futex_ref, bitset, - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx>, diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 6ee9ffaf37762..ea0fd5c5b0a3c 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -145,12 +145,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MacOsFutexTimeout::Relative { clock_op, timeout_op } => { let clock = this.read_scalar(clock_op)?.to_u32()?; let timeout = this.read_scalar(timeout_op)?.to_u64()?; - Some((clock, TimeoutAnchor::Relative, timeout)) + Some((clock, TimeoutStyle::Relative, timeout)) } MacOsFutexTimeout::Absolute { clock_op, timeout_op } => { let clock = this.read_scalar(clock_op)?.to_u32()?; let timeout = this.read_scalar(timeout_op)?.to_u64()?; - Some((clock, TimeoutAnchor::Absolute, timeout)) + Some((clock, TimeoutStyle::Absolute, timeout)) } }; @@ -168,12 +168,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let is_shared = flags == shared; - let timeout = clock_timeout.map(|(_, anchor, timeout)| { - // The only clock that is currently supported is the monotonic clock. + let deadline = clock_timeout.map(|(_, style, timeout)| { + // The only clock that is currently supported is the monotonic clock (checked above). // While the deadline argument of `os_sync_wait_on_address_with_deadline` // is actually not in nanoseconds but in the units of `mach_current_time`, // the two are equivalent in miri. - (TimeoutClock::Monotonic, anchor, Duration::from_nanos(timeout)) + this.machine.timeout(TimeoutClock::Monotonic, style, Duration::from_nanos(timeout)) }); // See the Linux futex implementation for why this fence exists. @@ -213,7 +213,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.futex_wait( futex_ref.clone(), u32::MAX, // bitset - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx>, diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 50904effa9fde..2e9a26974eff5 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -3,7 +3,6 @@ use std::io; use std::io::Read; use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4}; use std::sync::atomic::AtomicBool; -use std::time::Duration; use mio::event::Source; use mio::net::{TcpListener, TcpStream}; @@ -46,6 +45,13 @@ enum SocketState { /// For a socket created using the `connect` syscall, this is /// only reachable from the [`SocketState::Connecting`] state. Connected(TcpStream), + /// The SO_ERROR socket option has been set after calling + /// the `connect` syscall, indicating that the connection + /// attempt failed. By the POSIX specification, a socket is + /// is an unspecified state after a failed connection attempt + /// and thus nothing (except destroying the socket) should be + /// supported when a socket is in this state. + ConnectionFailed(TcpStream), } #[derive(Debug)] @@ -78,7 +84,10 @@ impl FileDescription for Socket { if matches!( &*self.state.borrow(), - SocketState::Listening(_) | SocketState::Connecting(_) | SocketState::Connected(_) + SocketState::Listening(_) + | SocketState::Connecting(_) + | SocketState::Connected(_) + | SocketState::ConnectionFailed(_) ) { // There exists an associated host socket so we need to deregister it // from the blocking I/O manager. @@ -374,6 +383,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "bind")?; let mut state = socket.state.borrow_mut(); @@ -412,6 +422,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "bind: socket is already bound and binding a socket \ multiple times is unsupported" ), + SocketState::ConnectionFailed(_) => unreachable!(), } interp_ok(Scalar::from_i32(0)) @@ -435,6 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "listen")?; let mut state = socket.state.borrow_mut(); @@ -461,6 +473,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SocketState::Connecting(_) | SocketState::Connected(_) => { throw_unsup_format!("listen: listening on a connected socket is unsupported") } + SocketState::ConnectionFailed(_) => unreachable!(), } interp_ok(Scalar::from_i32(0)) @@ -496,6 +509,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "accept4")?; if !matches!(*socket.state.borrow(), SocketState::Listening(_)) { throw_unsup_format!( @@ -590,6 +604,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "connect")?; match &*socket.state.borrow() { SocketState::Initial => { /* fall-through to below */ } @@ -633,15 +648,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dest = dest.clone(); this.ensure_connected( - socket, + socket.clone(), /* should_wait */ true, "connect", callback!( @capture<'tcx> { + socket: FileDescriptionRef, dest: MPlaceTy<'tcx> } |this, result: Result<(), ()>| { if result.is_err() { - this.set_last_error_and_return(LibcError("ENOTCONN"), &dest) + // An error occurred whilst connecting. We know + // that it has been consumed by `ensure_connected` + // and is now stored in `socket.error`. + let err = socket.error.take().unwrap(); + this.set_last_error_and_return(err, &dest) } else { this.write_scalar(Scalar::from_i32(0), &dest) } @@ -895,6 +915,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let socket = this.read_scalar(socket)?.to_i32()?; let level = this.read_scalar(level)?.to_i32()?; let option_name = this.read_scalar(option_name)?.to_i32()?; + let option_value_ptr = this.read_pointer(option_value)?; let socklen_layout = this.libc_ty_layout("socklen_t"); let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?; @@ -903,7 +924,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - let Some(_socket) = fd.downcast::() else { + let Some(socket) = fd.downcast::() else { // Man page specifies to return ENOTSOCK if `fd` is not a socket. return this.set_last_error_and_return_i32(LibcError("ENOTSOCK")); }; @@ -921,7 +942,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EINVAL")); } let option_value = - this.deref_pointer_as(option_value, this.machine.layouts.i32)?; + this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32); let _val = this.read_scalar(&option_value)?.to_i32()?; // We entirely ignore this value since we do not support signals anyway. @@ -934,7 +955,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Option value should be C-int which is usually 4 bytes. return this.set_last_error_and_return_i32(LibcError("EINVAL")); } - let option_value = this.deref_pointer_as(option_value, this.machine.layouts.i32)?; + let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32); let _val = this.read_scalar(&option_value)?.to_i32()?; // We entirely ignore this: std always sets REUSEADDR for us, and in the end it's more of a // hint to bypass some arbitrary timeout anyway. @@ -944,10 +965,74 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET", ); } + } else if level == this.eval_libc_i32("IPPROTO_IP") { + let opt_ip_ttl = this.eval_libc_i32("IP_TTL"); + + if option_name == opt_ip_ttl { + if option_len != 4 { + // Option value should be C-uint which is usually 4 bytes. + return this.set_last_error_and_return_i32(LibcError("EINVAL")); + } + let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.u32); + let ttl = this.read_scalar(&option_value)?.to_u32()?; + + let result = match &*socket.state.borrow() { + SocketState::Initial | SocketState::Bound(_) => + throw_unsup_format!( + "setsockopt: setting option IP_TTL on level IPPROTO_IP is only supported \ + on connected and listening sockets" + ), + SocketState::Listening(listener) => listener.set_ttl(ttl), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.set_ttl(ttl), + SocketState::ConnectionFailed(_) => unreachable!(), + }; + + return match result { + Ok(_) => interp_ok(Scalar::from_i32(0)), + Err(e) => this.set_last_error_and_return_i32(e), + }; + } else { + throw_unsup_format!( + "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP", + ); + } + } else if level == this.eval_libc_i32("IPPROTO_TCP") { + let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY"); + + if option_name == opt_tcp_nodelay { + if option_len != 4 { + // Option value should be C-int which is usually 4 bytes. + return this.set_last_error_and_return_i32(LibcError("EINVAL")); + } + let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32); + let nodelay = this.read_scalar(&option_value)?.to_i32()? != 0; + + let result = match &*socket.state.borrow() { + SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) => + throw_unsup_format!( + "setsockopt: setting option TCP_NODELAY on level IPPROTO_TCP is only supported \ + on connected sockets" + ), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.set_nodelay(nodelay), + SocketState::ConnectionFailed(_) => unreachable!(), + }; + + return match result { + Ok(_) => interp_ok(Scalar::from_i32(0)), + Err(e) => this.set_last_error_and_return_i32(e), + }; + } else { + throw_unsup_format!( + "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP" + ); + } } throw_unsup_format!( - "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed" + "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \ + and IPPROTO_TCP are allowed" ); } @@ -1002,17 +1087,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let opt_so_error = this.eval_libc_i32("SO_ERROR"); if option_name == opt_so_error { - // Because `TcpStream::take_error()` and `TcpListener::take_error()` consume the latest async - // error, we know that our stored `socket.error` is outdated when `TcpStream::take_error()`/ - // `TcpListener::take_error()` returns `Ok(Some(...))`. - // If they return `Ok(None)`, then we fall back to the stored `socket.error`. - let error = match &*socket.state.borrow() { - SocketState::Initial | SocketState::Bound(_) => socket.error.take(), - SocketState::Listening(listener) => - listener.take_error().unwrap_or(socket.error.take()), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.take_error().unwrap_or(socket.error.take()), + // Reading SO_ERROR should always return the latest async error. Because our stored + // `socket.error` could be outdated, we attempt to update it here. + this.update_last_error(&socket); + + let return_value = match socket.error.take() { + Some(err) => this.io_error_to_errnum(err)?.to_i32()?, + // If there is no error, we return 0 as the option value. + None => 0, }; + // Clear our own stored error -- it was either `take`n above or it is outdated. socket.error.replace(None); @@ -1021,12 +1105,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { socket.io_readiness.borrow_mut().error = false; this.update_epoll_active_events(socket, /* force_edge */ false)?; - let return_value = match error { - Some(err) => this.io_error_to_errnum(err)?.to_i32()?, - // If there is no error, we write 0 into the option value buffer. - None => 0, - }; - // Allocate new buffer on the stack with the `i32` layout. let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?; this.write_int(return_value, &value_buffer)?; @@ -1049,6 +1127,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SocketState::Listening(listener) => listener.ttl(), SocketState::Connecting(stream) | SocketState::Connected(stream) => stream.ttl(), + SocketState::ConnectionFailed(_) => unreachable!(), }; let ttl = match ttl { @@ -1065,9 +1144,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP", ); } + } else if level == this.eval_libc_i32("IPPROTO_TCP") { + let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY"); + + if option_name == opt_tcp_nodelay { + let nodelay = match &*socket.state.borrow() { + SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) => + throw_unsup_format!( + "getsockopt: reading option TCP_NODELAY on level IPPROTO_TCP is only supported \ + on connected sockets" + ), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.nodelay(), + SocketState::ConnectionFailed(_) => unreachable!(), + }; + + let nodelay = match nodelay { + Ok(nodelay) => nodelay, + Err(e) => return this.set_last_error_and_return_i32(e), + }; + + // Allocate new buffer on the stack with the `i32` layout. + let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?; + this.write_int(i32::from(nodelay), &value_buffer)?; + value_buffer + } else { + throw_unsup_format!( + "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP" + ); + } } else { throw_unsup_format!( - "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed" + "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \ + and IPPROTO_TCP are allowed" ) }; @@ -1120,6 +1229,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "getsockname")?; let state = socket.state.borrow(); @@ -1163,6 +1273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For non-bound sockets the POSIX manual says the returned address is unspecified. // Often this is 0.0.0.0:0 and thus we set it to this value. SocketState::Initial => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)), + SocketState::ConnectionFailed(_) => unreachable!(), }; this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname") @@ -1252,6 +1363,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "shutdown")?; let state = socket.state.borrow(); @@ -1605,12 +1717,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_read) if bytes_read < length && bytes_read > 0 => { - // We had a short read. (Note that reading 0 bytes is guaranteed to indicate EOF, - // and can never happen spuriously, so we have to exclude that case.) On Unix hosts - // using the `epoll` and `kqueue` backends, a short read means that the read buffer - // is empty. We update the readiness accordingly, which means that next time we see - // "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on + Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => { + // We had a short read (and were not peeking). (Note that reading 0 bytes is guaranteed + // to indicate EOF, and can never happen spuriously, so we have to exclude that case.) + // On Unix hosts using the `epoll` and `kqueue` backends, a short read means that the + // read buffer is empty. We update the readiness accordingly, which means that next time + // we see "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on // this behavior; see // if cfg!(any( @@ -1653,10 +1765,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Execute the provided callback function when the socket is either in // [`SocketState::Connected`] or an error occurred. /// If the socket is currently neither in the [`SocketState::Connecting`] nor - /// the [`SocketState::Connecting`] state, an ENOTCONN error is returned. - /// When the callback function is called with `Ok(_)`, then we're guaranteed + /// the [`SocketState::Connecting`] state, [`Err`] is returned. + /// When the callback function is called with [`Ok`], then we're guaranteed /// that the socket is in the [`SocketState::Connected`] state. /// + /// This method internally calls `ensure_not_failed` and thus an unsupported + /// error is thrown should `socket` be in [`SocketState::ConnectionFailed`]. + /// /// This function can optionally also block until either an error occurred or /// the socket reached the [`SocketState::Connected`] state. fn ensure_connected( @@ -1677,6 +1792,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } _ => { drop(state); + this.ensure_not_failed(&socket, foreign_name)?; return action.call(this, Err(())); } }; @@ -1688,16 +1804,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If we should wait until the connection is established, the timeout is `None`. // Otherwise, we use a zero duration timeout, i.e. we return immediately // (but we still go through the scheduler once -- which is fine). - let timeout = if should_wait { - None - } else { - Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO)) - }; + let deadline = + if should_wait { None } else { Some(this.machine.monotonic_clock.now().into()) }; this.block_thread_for_io( socket.clone(), BlockingIoInterest::Write, - timeout, + deadline, callback!( @capture<'tcx> { socket: FileDescriptionRef, @@ -1717,9 +1830,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The thread woke up because it's ready, indicating a writeable or error event. - let mut state = socket.state.borrow_mut(); - let stream = match &*state { - SocketState::Connecting(stream) => stream, + let state = socket.state.borrow(); + match &*state { + SocketState::Connecting(_) => { /* fall-through to below */ }, SocketState::Connected(_) => { drop(state); // This can happen because we blocked the thread: @@ -1732,25 +1845,19 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Since this thread just got rescheduled, it could be that another // thread realized that the connection failed and we're thus in // an "invalid state". + this.ensure_not_failed(&socket, foreign_name)?; return action.call(this, Err(())) } }; - // Manually check whether there were any errors since calling `connect`. - if let Ok(Some(err)) = stream.take_error() { - // There was an error during connecting and thus we - // return ENOTCONN. It's the program's responsibility - // to read SO_ERROR itself. + drop(state); - // Store the error such that we can return it when - // `getsockopt(SOL_SOCKET, SO_ERROR, ...)` is called on the socket. - socket.error.replace(Some(err)); + // Set `socket.error` if `socket` currently has an error. + this.update_last_error(&socket); - // Go back to initial state since the only way of getting into the - // `Connecting` state is from the `Initial` state and at this point - // we know that the connection won't be established anymore. - *state = SocketState::Initial; - drop(state); + if socket.error.borrow().is_some() { + // There was an error during connecting. + // It's the program's responsibility to read SO_ERROR itself. return action.call(this, Err(())) } @@ -1772,6 +1879,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The connection is established. // Temporarily use dummy state to take ownership of the stream. + let mut state = socket.state.borrow_mut(); let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else { // At the start of the function we ensured that we're currently connecting. unreachable!() @@ -1783,6 +1891,64 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ), ) } + + /// Ensure that `socket` is not in the [`SocketState::ConnectionFailed`] state. + /// If `socket` is currently in [`SocketState::ConnectionFailed`], an unsupported + /// error is thrown. + fn ensure_not_failed( + &self, + socket: &FileDescriptionRef, + foreign_name: &'static str, + ) -> InterpResult<'tcx> { + if let SocketState::ConnectionFailed(_) = &*socket.state.borrow() { + throw_unsup_format!( + "{foreign_name}: sockets are in an unspecified state after a failed `connect`; \ + any operation on such a socket is thus unsupported" + ); + } else { + interp_ok(()) + } + } + + /// Check whether the underlying host socket of `socket` contains an error. + /// If there is an error, we store it in `socket.error`. + /// + /// Should `socket` be in the [`SocketState::Connecting`] state whilst there is + /// an error on the host socket, we transition into the [`SocketState::ConnectionFailed`] + /// state because we know that `socket` can no longer successfully establish a + /// connection. + fn update_last_error(&self, socket: &FileDescriptionRef) { + let mut state = socket.state.borrow_mut(); + + let new_error = match &*state { + SocketState::Listening(listener) => + listener.take_error().expect("Reading SO_ERROR should not fail"), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.take_error().expect("Reading SO_ERROR should not fail"), + SocketState::Initial | SocketState::Bound(_) | SocketState::ConnectionFailed(_) => None, + }; + + let Some(new_error) = new_error else { return }; + + // Store the error such that we can return it when + // `getsockopt(SOL_SOCKET, SO_ERROR, ...)` is called on the socket. + socket.error.replace(Some(new_error)); + + if matches!(&*state, SocketState::Connecting(_)) { + // After reading an error on a connecting socket, we know that + // the connection won't be established anymore. By the POSIX + // specification, the socket is now in an unspecified state. + // We thus change the socket state to `ConnectionFailed`. + + // Temporarily use dummy state to take ownership of the stream. + let SocketState::Connecting(stream) = + std::mem::replace(&mut *state, SocketState::Initial) + else { + unreachable!() + }; + *state = SocketState::ConnectionFailed(stream); + } + } } impl VisitProvenance for FileDescriptionRef { @@ -1796,7 +1962,9 @@ impl SourceFileDescription for Socket { let mut state = self.state.borrow_mut(); match &mut *state { SocketState::Listening(listener) => f(listener), - SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream), + SocketState::Connecting(stream) + | SocketState::Connected(stream) + | SocketState::ConnectionFailed(stream) => f(stream), // We never try adding a socket which is not backed by a real socket to the poll registry. _ => unreachable!(), } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 1b65dea982464..4e351c1571218 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -920,22 +920,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); }; - let (clock, anchor) = if macos_relative_np { + let (clock, style) = if macos_relative_np { // `pthread_cond_timedwait_relative_np` always measures time against the // monotonic clock, regardless of the condvar clock. - (TimeoutClock::Monotonic, TimeoutAnchor::Relative) + (TimeoutClock::Monotonic, TimeoutStyle::Relative) } else { if data.clock == TimeoutClock::RealTime { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; } - (data.clock, TimeoutAnchor::Absolute) + (data.clock, TimeoutStyle::Absolute) }; this.condvar_wait( data.condvar_ref, mutex_ref, - Some((clock, anchor, duration)), + Some(this.machine.timeout(clock, style, duration)), Scalar::from_i32(0), this.eval_libc("ETIMEDOUT"), // retval_timeout dest.clone(), diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 14562450e6e07..1b75d6647c831 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -209,11 +209,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let size = Size::from_bytes(size); - let timeout = if timeout_ms == this.eval_windows_u32("c", "INFINITE") { + let deadline = if timeout_ms == this.eval_windows_u32("c", "INFINITE") { None } else { let duration = Duration::from_millis(timeout_ms.into()); - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)) + Some(this.machine.monotonic_clock.now().add_lossy(duration).into()) }; // See the Linux futex implementation for why this fence exists. @@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.futex_wait( futex_ref, u32::MAX, // bitset - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx> diff --git a/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs new file mode 100644 index 0000000000000..babd2d8410207 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs @@ -0,0 +1,68 @@ +//@only-target: linux android illumos +//@compile-flags: -Zmiri-disable-isolation + +#![feature(io_error_inprogress)] + +#[path = "../../utils/libc.rs"] +mod libc_utils; + +use std::io::ErrorKind; + +use libc_utils::epoll::*; +use libc_utils::*; + +/// Test calling `connect` after the first `connect` failed. +fn main() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // We cannot attempt to connect to a localhost address because + // it could be the case that a socket from another test is + // currently listening on `localhost:12321` because we bind to + // random ports everywhere. For `127.0.1.1` we know that it's a loopback + // address and thus exists but because it's not the standard loopback + // address we also assume that nothing is bound to it. + // The port `12321` is just a random non-zero port because Windows + // and Apple hosts return EADDRNOTAVAIL when attempting to connect to + // a zero port. + let addr = net::sock_addr_ipv4([127, 0, 1, 1], 12321); + + // Non-blocking connect should fail with EINPROGRESS. + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InProgress); + + // Add interest for client socket. + epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLET | libc::EPOLLERR).unwrap(); + + // Wait until the socket has an error. + check_epoll_wait::<8>( + epfd, + &[Ev { events: libc::EPOLLERR | EPOLLOUT | EPOLLHUP, data: client_sockfd }], + -1, + ); + + let errno = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); + // Depending on the host we receive different error kinds. Thus, we only check + // that it's a nonzero error code. + assert!(errno != 0); + + // Ensure that error readiness is cleared after reading SO_ERROR. + let readiness = current_epoll_readiness::<8>(client_sockfd, EPOLLET | EPOLLOUT | EPOLLERR); + assert!(readiness & EPOLLERR == 0); + + unsafe { + //~vERROR: sockets are in an unspecified state after a failed `connect` + libc::connect( + client_sockfd, + (&addr as *const libc::sockaddr_in).cast(), + size_of::() as libc::socklen_t, + ) + }; +} diff --git a/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr new file mode 100644 index 0000000000000..3743c3ca58f22 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr @@ -0,0 +1,16 @@ +error: unsupported operation: connect: sockets are in an unspecified state after a failed `connect`; any operation on such a socket is thus unsupported + --> tests/fail-dep/libc/socket-connect-after-failed-connection.rs:LL:CC + | +LL | / libc::connect( +LL | | client_sockfd, +LL | | (&addr as *const libc::sockaddr_in).cast(), +LL | | size_of::() as libc::socklen_t, +LL | | ) + | |_________^ unsupported operation occurred here + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr index 4f06a1afa50fe..afc2cb214842e 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr @@ -2,7 +2,7 @@ error: Undefined Behavior: memory access failed: ALLOC has been freed, so this p --> tests/fail/dangling_pointers/dangling_primitive.rs:LL:CC | LL | dbg!(*ptr); - | ^^^^ Undefined Behavior occurred here + | ^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr index 88d5694c4736d..364a4b4a74418 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr @@ -7,7 +7,7 @@ error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is unin --> tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC | LL | dbg!(x.0); - | ^^^ Undefined Behavior occurred here + | ^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr index 282dc19eea791..bdd2241c70707 100644 --- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4.. 8 -| Act | Act | └─┬── +| Unq | Unq | └─┬── | Frz |?Cel | └──── ────────────────────────────────────────────────── error: Undefined Behavior: write access through (a) at ALLOC[0x0] is forbidden diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr index 282dc19eea791..bdd2241c70707 100644 --- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4.. 8 -| Act | Act | └─┬── +| Unq | Unq | └─┬── | Frz |?Cel | └──── ────────────────────────────────────────────────── error: Undefined Behavior: write access through (a) at ALLOC[0x0] is forbidden diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr index 29397a8fd0bc7..f1bb93354ff90 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── | ReIM| ├─┬── | ReIM| │ └─┬── diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr index 3bddd2ce1de63..c86d70bcba287 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├─┬── | Res | │ └─┬── diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs index 4793ee560fca4..f162c306784d0 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs @@ -1,7 +1,5 @@ //@only-target: linux freebsd # these are Linux/FreeBSD-specific APIs //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4 -#![feature(io_error_more)] -#![feature(pointer_is_aligned_to)] use std::mem::{size_of, size_of_val}; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index d9a2be7fc5014..8865c605a3b65 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -89,7 +89,7 @@ fn test_notification_after_timeout() { check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[0] }], 10); } -// This test shows a data_race before epoll had vector clocks added. +// This test shows a data race before epoll had vector clocks added. fn test_epoll_race() { // Create an epoll instance. let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); @@ -99,7 +99,7 @@ fn test_epoll_race() { let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Register eventfd with the epoll instance. - epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLET).unwrap(); static mut VAL: u8 = 0; let thread1 = thread::spawn(move || { @@ -109,13 +109,10 @@ fn test_epoll_race() { write_all(fd, &1_u64.to_ne_bytes()).unwrap(); }); thread::yield_now(); - // epoll_wait for the event to happen. - check_epoll_wait::<8>(epfd, &[Ev { events: (libc::EPOLLIN | libc::EPOLLOUT), data: fd }], -1); + // epoll_wait for EPOLLIN. + check_epoll_wait::<8>(epfd, &[Ev { events: libc::EPOLLIN, data: fd }], -1); // Read from the static mut variable. - #[allow(static_mut_refs)] - unsafe { - assert_eq!(VAL, 1) - }; + assert_eq!(unsafe { VAL }, 1); thread1.join().unwrap(); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index 56d215d0ed633..e86c70b590b36 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -5,7 +5,10 @@ // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] -use std::thread; +use std::{io, thread}; + +#[path = "../../utils/libc.rs"] +mod libc_utils; fn main() { test_read_write(); @@ -19,15 +22,16 @@ fn main() { test_two_threads_blocked_on_eventfd(); } -fn read_bytes(fd: i32, buf: &mut [u8; N]) -> i32 { - let res: i32 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), N).try_into().unwrap() }; - return res; +// We want to do individual read/write calls here so we avoid read_exact/write_all. + +fn read_bytes(fd: i32, buf: &mut [u8; N]) -> io::Result { + libc_utils::errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), N) }) + .map(|len| len.try_into().unwrap()) } -fn write_bytes(fd: i32, data: [u8; N]) -> i32 { - let res: i32 = - unsafe { libc::write(fd, data.as_ptr() as *const libc::c_void, N).try_into().unwrap() }; - return res; +fn write_bytes(fd: i32, data: [u8; N]) -> io::Result { + libc_utils::errno_result(unsafe { libc::write(fd, data.as_ptr() as *const libc::c_void, N) }) + .map(|len| len.try_into().unwrap()) } fn test_read_write() { @@ -35,64 +39,60 @@ fn test_read_write() { let fd = unsafe { libc::eventfd(0, flags) }; let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); // Write 1 to the counter. - let res = write_bytes(fd, sized_8_data); + let res = write_bytes(fd, sized_8_data).unwrap(); + assert_eq!(res, 8); + // Write 0 to the counter, + let res = write_bytes(fd, [0u8; 8]).unwrap(); assert_eq!(res, 8); // Read 1 from the counter. let mut buf: [u8; 8] = [0; 8]; - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // Read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); // Check the value of counter read. - let counter = u64::from_ne_bytes(buf); - assert_eq!(counter, 1); + assert_eq!(u64::from_ne_bytes(buf), 1); - // After read, the counter is currently 0, read counter 0 should fail with return - // value -1. + // After read, the counter is currently 0, read counter 0 should fail with EAGAIN. let mut buf: [u8; 8] = [0; 8]; - let res = read_bytes(fd, &mut buf); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EAGAIN)); - assert_eq!(res, -1); - - // Write with supplied buffer bigger than 8 bytes should be allowed. - let sized_9_data: [u8; 9]; - if cfg!(target_endian = "big") { - // Adjust the data based on the endianness of host system. - sized_9_data = [0, 0, 0, 0, 0, 0, 0, 1, 0]; - } else { - sized_9_data = [1, 0, 0, 0, 0, 0, 0, 0, 0]; - } - let res = write_bytes(fd, sized_9_data); - assert_eq!(res, 8); + let err = read_bytes(fd, &mut buf).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EAGAIN)); + + // Write with supplied buffer bigger than 8 bytes should be allowed according to the docs, + // but tests on real systems indicate that it fails. + let sized_9_data = [0u8; 9]; + let err = write_bytes(fd, sized_9_data).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); // Read with supplied buffer smaller than 8 bytes should fail with return // value -1. let mut buf: [u8; 7] = [1; 7]; - let res = read_bytes(fd, &mut buf); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(res, -1); + let err = read_bytes(fd, &mut buf).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); // Write with supplied buffer smaller than 8 bytes should fail with return // value -1. let size_7_data: [u8; 7] = [1; 7]; - let res = write_bytes(fd, size_7_data); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(res, -1); + let err = write_bytes(fd, size_7_data).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); + + // Do two valid writes so we have something to read for the next test. + let res = write_bytes(fd, sized_8_data).unwrap(); + assert_eq!(res, 8); + let res = write_bytes(fd, sized_8_data).unwrap(); + assert_eq!(res, 8); // Read with supplied buffer bigger than 8 bytes should be allowed. let mut buf: [u8; 9] = [1; 9]; - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); assert_eq!(res, 8); + let buf: &[u8; 8] = (&buf[..8]).try_into().unwrap(); + assert_eq!(u64::from_ne_bytes(*buf), 2); // Write u64::MAX should fail. let u64_max_bytes: [u8; 8] = [255; 8]; - let res = write_bytes(fd, u64_max_bytes); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(res, -1); + let err = write_bytes(fd, u64_max_bytes).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); } fn test_race() { @@ -101,7 +101,7 @@ fn test_race() { let fd = unsafe { libc::eventfd(0, flags) }; let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); @@ -110,7 +110,7 @@ fn test_race() { }); unsafe { VAL = 1 }; let data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = write_bytes(fd, data); + let res = write_bytes(fd, data).unwrap(); // write returns number of bytes written, which is always 8. assert_eq!(res, 8); thread::yield_now(); @@ -135,7 +135,7 @@ fn test_blocking_read() { let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will block. - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); @@ -145,7 +145,7 @@ fn test_blocking_read() { // Pass control to thread1 so it can block on eventfd `read`. thread::yield_now(); // Write 1 to the counter to unblock thread1. - let res = write_bytes(fd, sized_8_data); + let res = write_bytes(fd, sized_8_data).unwrap(); assert_eq!(res, 8); thread1.join().unwrap(); } @@ -176,7 +176,7 @@ fn test_blocking_write() { // Pass control to thread1 so it can block on eventfd `write`. thread::yield_now(); // This will unblock previously blocked eventfd read. - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); @@ -222,7 +222,7 @@ fn test_two_threads_blocked_on_eventfd() { let thread3 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will unblock previously blocked eventfd read. - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs index f03e1ad8641de..fd67afbe83c4b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs @@ -2,9 +2,6 @@ //@ignore-host: windows # needs unix PermissionExt //@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(io_error_uncategorized)] - use std::ffi::{CStr, CString}; use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a388943f922ff..9cc8685827077 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -1,15 +1,13 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(io_error_uncategorized)] - use std::ffi::{CStr, CString, OsString}; -use std::fs::{File, canonicalize, remove_file}; +use std::fs::{File, canonicalize, create_dir, remove_dir, remove_file}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; use std::path::PathBuf; +use std::ptr; #[path = "../../utils/mod.rs"] mod utils; @@ -17,6 +15,8 @@ mod utils; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::errno_result; + fn main() { test_dup(); test_dup_stdout_stderr(); @@ -58,6 +58,16 @@ fn main() { test_statx_on_file_descriptor(); #[cfg(target_os = "linux")] test_statx_empty_path_on_pipe(); + test_readv(); + test_readv_empty_bufs(); + #[cfg(not(target_os = "solaris"))] + test_preadv(); + test_pread(); + test_writev(); + test_writev_empty_bufs(); + #[cfg(not(target_os = "solaris"))] + test_pwritev(); + test_pwrite(); } #[cfg(target_os = "linux")] @@ -67,10 +77,12 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &std::fs::Metadata, ex let mask = stx.stx_mask; // Guaranteed by the shim on any Linux target. - assert!(mask & libc::STATX_TYPE != 0); assert!(mask & libc::STATX_SIZE != 0); assert_eq!(stx.stx_size, expected_size); - assert_eq!((stx.stx_mode as u32) & libc::S_IFMT, libc::S_IFREG); + assert!(mask & libc::STATX_TYPE != 0); + assert_eq!((stx.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFREG); + assert!(mask & libc::STATX_MODE != 0); + assert_ne!((stx.stx_mode as libc::mode_t) & !libc::S_IFMT, 0); // Host-dependent enrichment: only assert when the mask says the field is real. if mask & libc::STATX_INO != 0 { @@ -89,9 +101,6 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &std::fs::Metadata, ex assert_eq!(stx.stx_blocks, meta.blocks()); } - // We don't support non-S_IFMT bits in stx_mode. - assert_eq!(mask & libc::STATX_MODE, 0); - // Do not assert stx_blksize and stx_dev_* : there are no mask bits for them. } @@ -177,18 +186,21 @@ fn test_statx_empty_path_on_pipe() { let statx_buf = statx_buf.assume_init(); - assert_ne!(statx_buf.stx_mask & libc::STATX_TYPE, 0); assert_ne!(statx_buf.stx_mask & libc::STATX_SIZE, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_MODE, 0); - assert_eq!((statx_buf.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFIFO); assert_eq!(statx_buf.stx_size, 0); - - // Synthetic metadata must not advertise host-only fields. - assert_eq!(statx_buf.stx_mask & libc::STATX_INO, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_NLINK, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_UID, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_GID, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_BLOCKS, 0); + assert_ne!(statx_buf.stx_mask & libc::STATX_TYPE, 0); + assert_eq!((statx_buf.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFIFO); + assert_ne!(statx_buf.stx_mask & libc::STATX_MODE, 0); + assert_ne!((statx_buf.stx_mode as libc::mode_t) & !libc::S_IFMT, 0); + + if cfg!(miri) { + // Synthetic metadata must not advertise host-only fields. + assert_eq!(statx_buf.stx_mask & libc::STATX_INO, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_NLINK, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_UID, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_GID, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_BLOCKS, 0); + } errno_check(libc::close(fds[0])); errno_check(libc::close(fds[1])); @@ -325,13 +337,16 @@ fn test_ftruncate>( #[cfg(target_os = "linux")] fn test_o_tmpfile_flag() { + if !cfg!(miri) { + return; // checks miri-specific behavior + } + use std::fs::{OpenOptions, create_dir}; use std::os::unix::fs::OpenOptionsExt; let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); // test that the `O_TMPFILE` custom flag gracefully errors instead of stopping execution assert_eq!( - Some(libc::EOPNOTSUPP), OpenOptions::new() .read(true) .write(true) @@ -339,14 +354,22 @@ fn test_o_tmpfile_flag() { .open(dir_path) .unwrap_err() .raw_os_error(), + Some(libc::EOPNOTSUPP), ); } fn test_posix_mkstemp() { + use std::env; use std::ffi::OsStr; use std::os::unix::io::FromRawFd; use std::path::Path; + // We want to test `mkstemp` on a relative name, so we cd to a tempdir and later cd back. + let old_cwd = env::current_dir().unwrap(); + let dir_path = utils::prepare_dir("miri_test_libc_readdir"); + create_dir(&dir_path).expect("create_dir failed"); + env::set_current_dir(&dir_path).unwrap(); + let valid_template = "fooXXXXXX"; // C needs to own this as `mkstemp(3)` says: // "Since it will be modified, `template` must not be a string constant, but @@ -354,12 +377,12 @@ fn test_posix_mkstemp() { // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`. let ptr = CString::new(valid_template).unwrap().into_raw(); let fd = unsafe { libc::mkstemp(ptr) }; + assert!(fd >= 0, "mkstemp failed"); // Take ownership back in Rust to not leak memory. let slice = unsafe { CString::from_raw(ptr) }; - assert!(fd > 0); let osstr = OsStr::from_bytes(slice.to_bytes()); let path: &Path = osstr.as_ref(); - let name = path.file_name().unwrap().to_string_lossy(); + let name = path.to_string_lossy(); assert!(name.ne("fooXXXXXX")); assert!(name.starts_with("foo")); assert_eq!(name.len(), 9); @@ -369,6 +392,9 @@ fn test_posix_mkstemp() { ); let file = unsafe { File::from_raw_fd(fd) }; assert!(file.set_len(0).is_ok()); + // Cleanup. Also checks that the filename actually exists. + drop(file); + remove_file(path).unwrap(); let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; for t in invalid_templates { @@ -382,6 +408,8 @@ fn test_posix_mkstemp() { assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); } + + env::set_current_dir(old_cwd).unwrap(); } /// Test allocating variant of `realpath`. @@ -751,7 +779,6 @@ fn test_ioctl() { fn test_opendir_closedir() { // dir should exist - use std::fs::{create_dir, remove_dir}; let path = utils::prepare_dir("miri_test_libc_opendir_closedir"); create_dir(&path).expect("create_dir failed"); let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); @@ -846,3 +873,277 @@ pub fn check_stat_fields(stat: &libc::stat) { let _st_mtime_nsec = stat.st_mtime_nsec; let _st_ctime_nsec = stat.st_ctime_nsec; } + +/// Test vectored reads with multiple buffers. +fn test_readv() { + let file_contents = [1u8, 2, 3, 4, 5, 6]; + let path = utils::prepare_with_content("pass-libc-readv.txt", &file_contents); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut buffer = [0u8; 4]; + let (buffer1, buffer2) = buffer.split_at_mut(2); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let bytes_read = unsafe { + errno_result(libc::readv(fd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() as usize + }; + + // The vectored read should read at least one byte. + assert!(bytes_read > 0); + assert_eq!(&buffer[0..bytes_read], &file_contents[0..bytes_read]); +} + +/// Test that vectored reads without any buffers return zero. +fn test_readv_empty_bufs() { + let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + unsafe { assert_eq!(errno_result(libc::readv(fd, ptr::null::(), 0)).unwrap(), 0) }; +} + +/// Test vectored reads with multiple buffers and a byte offset. +/// +/// **Note**: We skip this test on Solaris targets because Solaris +/// doesn't have `preadv`. +#[cfg(not(target_os = "solaris"))] +fn test_preadv() { + let file_contents = [1u8, 2, 3, 4, 5, 6]; + let path = utils::prepare_with_content("pass-libc-preadv.txt", &file_contents); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut buffer = [0u8; 4]; + let (buffer1, buffer2) = buffer.split_at_mut(2); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + // Read with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_read = unsafe { + errno_result(libc::preadv( + fd, + iov.as_ptr(), + iov.len() as libc::c_int, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + + // The vectored read should read at least one byte. + assert!(bytes_read > 0); + // The vectored read should start at the provided byte offset. + assert_eq!(&buffer[0..bytes_read], &file_contents[OFFSET..(bytes_read + OFFSET)]); +} + +/// Test reading with an offset. +fn test_pread() { + let file_contents = [1u8, 2, 3, 4, 5, 6]; + let path = utils::prepare_with_content("pass-libc-pread.txt", &file_contents); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut buffer = [0u8; 2]; + + // Read with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_read = unsafe { + errno_result(libc::pread( + fd, + buffer.as_mut_ptr().cast(), + buffer.len() as libc::size_t, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + + // We should read at least one byte. + assert!(bytes_read > 0); + // The read should start at the provided byte offset. + assert_eq!(&buffer[0..bytes_read], &file_contents[OFFSET..(bytes_read + OFFSET)]); +} + +/// Test vectored writes with multiple buffers. +fn test_writev() { + let path = utils::prepare_with_content("pass-libc-writev.txt", &[]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + + let mut write_buffer = [1u8, 2, 3, 4, 5, 6]; + let (buffer1, buffer2) = write_buffer.split_at_mut(3); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let bytes_written = unsafe { + errno_result(libc::writev(fd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() as usize + }; + // The vectored write should write at least one byte. + assert!(bytes_written > 0); + + // Open the FD again in readonly mode and with an unadvanced pointer. + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut read_buffer = [0u8; 16]; + unsafe { + libc_utils::read_exact_generic( + read_buffer.as_mut_ptr().cast(), + bytes_written as libc::size_t, + libc_utils::Retry::NoRetry, + |buf, count| libc::read(fd, buf, count), + ) + .unwrap() + }; + + assert_eq!(&write_buffer[0..bytes_written], &read_buffer[0..bytes_written]); +} + +/// Test that vectored writes without any buffers return zero. +fn test_writev_empty_bufs() { + let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + unsafe { + assert_eq!(errno_result(libc::writev(fd, ptr::null::(), 0)).unwrap(), 0) + }; +} + +/// Test vectored writes with multiple buffers and a byte offset. +/// +/// **Note**: We skip this test on Solaris targets because Solaris +/// doesn't have `pwritev`. +#[cfg(not(target_os = "solaris"))] +fn test_pwritev() { + let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + + let mut write_buffer = [1u8, 2, 3, 4, 5, 6]; + let (buffer1, buffer2) = write_buffer.split_at_mut(3); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + // Write with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_written = unsafe { + errno_result(libc::pwritev( + fd, + iov.as_ptr(), + iov.len() as libc::c_int, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + // The vectored write should write at least one byte. + assert!(bytes_written > 0); + + // Open the FD again in readonly mode and with an unadvanced pointer. + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut read_buffer = [0u8; 16]; + // Read offset + bytes written. + unsafe { + libc_utils::read_exact_generic( + read_buffer.as_mut_ptr().cast(), + (bytes_written + OFFSET) as libc::size_t, + libc_utils::Retry::NoRetry, + |buf, count| libc::read(fd, buf, count), + ) + .unwrap() + }; + + // The vectored write should start at the provided byte offset. + assert_eq!(&write_buffer[0..bytes_written], &read_buffer[OFFSET..(bytes_written + OFFSET)]); +} + +/// Test writing with an offset. +fn test_pwrite() { + let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + + let write_buffer = [1u8, 2, 3, 4, 5, 6]; + + // Write with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_written = unsafe { + errno_result(libc::pwrite( + fd, + write_buffer.as_ptr().cast(), + write_buffer.len() as libc::size_t, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + // We should write at least one byte. + assert!(bytes_written > 0); + + // Open the FD again in readonly mode and with an unadvanced pointer. + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut read_buffer = [0u8; 16]; + // Read offset + bytes written. + unsafe { + libc_utils::read_exact_generic( + read_buffer.as_mut_ptr().cast(), + (bytes_written + OFFSET) as libc::size_t, + libc_utils::Retry::NoRetry, + |buf, count| libc::read(fd, buf, count), + ) + .unwrap() + }; + + // The write should start at the provided byte offset. + assert_eq!(&write_buffer[0..bytes_written], &read_buffer[OFFSET..(bytes_written + OFFSET)]); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs index 1eac3d25e56a1..10d756e05104b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs @@ -1,7 +1,5 @@ //@ignore-target: windows # only very limited libc on Windows //@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(pointer_is_aligned_to)] use std::mem::transmute; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 08f1ee91b1b1f..9c8c5be647d4c 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -25,6 +25,7 @@ fn main() { test_shutdown_read(); test_shutdown_write(); test_readiness_after_short_read(); + test_readiness_after_short_peek(); test_readiness_after_short_write(); } @@ -505,6 +506,55 @@ fn test_readiness_after_short_read() { }; } +/// Test that Miri doesn't remove the readable readiness after a short peek. +fn test_readiness_after_short_peek() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // Write some bytes into the peer socket. + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + // FIXME: Changes in host I/O readiness are only processed when entering the scheduler. + // Ensure that we process the effects if the `write_all` by yielding the current (only) thread. + // + thread::yield_now(); + + // `buffer` is intentionally bigger than `TEST_BYTES.len()` to trigger a short peek. + let mut buffer = [0; 128]; + let bytes_read = unsafe { + errno_result(libc::recv( + client_sockfd, + buffer.as_mut_ptr().cast(), + buffer.len(), + libc::MSG_PEEK, + )) + .unwrap() + } as usize; + assert_eq!(bytes_read, TEST_BYTES.len()); + + // FIXME(#5047): same as above. + thread::yield_now(); + + // Ensure that the readable readiness is still set. + assert_eq!(current_epoll_readiness::<8>(client_sockfd, EPOLLIN | EPOLLET), EPOLLIN); + + // We should be able to read the buffer without blocking indefinitely. + let bytes_read = unsafe { + errno_result(libc::recv(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len(), 0)) + .unwrap() + } as usize; + assert_eq!(bytes_read, TEST_BYTES.len()); +} + /// Test that Miri correctly removes the writable readiness or emits a new edge after a short write. fn test_readiness_after_short_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index d9200c818608c..1ecfba3ff6ff0 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -7,8 +7,8 @@ mod libc_utils; mod utils; use std::io::ErrorKind; -use std::thread; use std::time::Duration; +use std::{ptr, thread}; use libc_utils::*; @@ -35,8 +35,11 @@ fn main() { test_listen(); test_accept_connect(); + test_connect_error(); test_send_peek_recv(); test_write_read(); + test_readv(); + test_writev(); test_getsockname_ipv4(); test_getsockname_ipv4_random_port(); @@ -256,6 +259,27 @@ fn test_accept_connect() { server_thread.join().unwrap(); } +/// Test connecting to an address where nothing is listening and ensure the error matches what +/// the standard library expects. +fn test_connect_error() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + // Connecting to a zero port fails on all host platforms. + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + // Ensure that we fail for the same reasons as the standard library expects. + assert!(matches!( + err.kind(), + ErrorKind::ConnectionRefused + | ErrorKind::InvalidInput + | ErrorKind::AddrInUse + | ErrorKind::AddrNotAvailable + | ErrorKind::NetworkUnreachable + )); +} + /// Test sending bytes into a connected stream and then peeking and receiving /// them from the other end. /// We especially want to test that the peeking doesn't remove the bytes from @@ -342,6 +366,77 @@ fn test_write_read() { server_thread.join().unwrap(); } +/// Test vectored reads with multiple buffers on a connected socket. +fn test_readv() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + let mut buffer = [0u8; TEST_BYTES.len()]; + let (buffer1, buffer2) = buffer.split_at_mut(2); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let num = unsafe { + errno_result(libc::readv(client_sockfd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() + }; + assert_eq!(num as usize, TEST_BYTES.len()); + // The vectored read should read the entire buffer because we don't have + // short reads on sockets. + assert_eq!(&buffer, TEST_BYTES); +} + +/// Test vectored writes with multiple buffers on a connected socket. +fn test_writev() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + let mut write_buffer = TEST_BYTES.to_owned(); + let (buffer1, buffer2) = write_buffer.split_at_mut(3); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let num = unsafe { + errno_result(libc::writev(client_sockfd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() + }; + assert_eq!(num as usize, TEST_BYTES.len()); + + let mut buffer = [0u8; TEST_BYTES.len()]; + libc_utils::read_exact(peerfd, &mut buffer).unwrap(); + // The vectored write should write the entire buffer because we don't have + // short writes on sockets. + assert_eq!(&buffer, TEST_BYTES); +} + /// Test the `getsockname` syscall on an IPv4 socket which is bound. /// The `getsockname` syscall should return the same address as to /// which the socket was bound to. @@ -425,10 +520,8 @@ fn test_getsockname_ipv4_connect() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - // Spawn the server thread. - let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); - net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); let (_, sock_addr) = net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(client_sockfd, storage, len) @@ -443,8 +536,6 @@ fn test_getsockname_ipv4_connect() { assert_eq!(addr.sin_family, sock_addr.sin_family); assert_ne!(addr.sin_addr.s_addr, sock_addr.sin_addr.s_addr); assert!(sock_addr.sin_port > 0); - - server_thread.join().unwrap(); } /// Test the `getsockname` syscall on an IPv6 socket which is bound. @@ -485,10 +576,8 @@ fn test_getpeername_ipv4() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - // Spawn the server thread. - let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); - net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); let (_, peer_addr) = net::sockname_ipv4(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) @@ -498,8 +587,6 @@ fn test_getpeername_ipv4() { assert_eq!(addr.sin_family, peer_addr.sin_family); assert_eq!(addr.sin_port, peer_addr.sin_port); assert_eq!(addr.sin_addr.s_addr, peer_addr.sin_addr.s_addr); - - server_thread.join().unwrap(); } /// Test the `getpeername` syscall on an IPv6 socket. @@ -510,10 +597,8 @@ fn test_getpeername_ipv6() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; - // Spawn the server thread. - let server_thread = thread::spawn(move || net::accept_ipv6(server_sockfd).unwrap()); - net::connect_ipv6(client_sockfd, addr).unwrap(); + net::accept_ipv6(server_sockfd).unwrap(); let (_, peer_addr) = net::sockname_ipv6(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) @@ -525,8 +610,6 @@ fn test_getpeername_ipv6() { assert_eq!(addr.sin6_flowinfo, peer_addr.sin6_flowinfo); assert_eq!(addr.sin6_scope_id, peer_addr.sin6_scope_id); assert_eq!(addr.sin6_addr.s6_addr, peer_addr.sin6_addr.s6_addr); - - server_thread.join().unwrap(); } /// Test shutting down TCP streams. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index 141e0009101aa..f315d2ab117f9 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -66,7 +66,7 @@ fn test_posix_gettimeofday() { assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - // Test that non-null tz returns an error. + // Test that non-null tz returns an error (because we don't support it). let mut tz = mem::MaybeUninit::::uninit(); let tz_ptr = tz.as_mut_ptr(); let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index e0da9f63876f1..b249c065318c2 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -2,13 +2,16 @@ #![feature(io_error_more)] #![feature(io_error_uncategorized)] +#![cfg_attr(unix, feature(unix_file_vectored_at))] use std::collections::BTreeMap; use std::ffi::OsString; use std::fs::{ self, File, OpenOptions, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, }; -use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; +use std::io::{ + Error, ErrorKind, IoSlice, IoSliceMut, IsTerminal, Read, Result, Seek, SeekFrom, Write, +}; use std::path::Path; #[path = "../../utils/mod.rs"] @@ -39,6 +42,9 @@ fn main() { test_pread_pwrite(); #[cfg(not(any(target_os = "solaris", target_os = "android")))] test_flock(); + test_readv_writev(); + #[cfg(all(unix, not(any(target_os = "solaris", target_os = "android"))))] + test_preadv_pwritev(); } } @@ -400,7 +406,10 @@ fn test_pread_pwrite() { assert_eq!(&buf1, b" m"); } -// The standard library does not support this operation on Solaris, Android +// The standard library does not support this operation on Android +// (https://github.com/rust-lang/rust/issues/148325). +// Miri does not support the way this is implemented on Solaris +// (https://github.com/rust-lang/miri/issues/5038). #[cfg(not(any(target_os = "solaris", target_os = "android")))] fn test_flock() { let bytes = b"Hello, World!\n"; @@ -424,3 +433,82 @@ fn test_flock() { // Unlock exclusive lock file1.unlock().unwrap(); } + +/// Test vectored reads and vectored writes. +fn test_readv_writev() { + let bytes = b"hello world!"; + let path = utils::prepare_with_content("miri_test_fs_readv_writev.txt", bytes); + let mut f = OpenOptions::new().read(true).write(true).open(path).unwrap(); + + let mut read_buffer = [0u8; 10]; + let (buffer1, buffer2) = read_buffer.split_at_mut(5); + + let bytes_read = + f.read_vectored(&mut [IoSliceMut::new(buffer1), IoSliceMut::new(buffer2)]).unwrap(); + + // Vectored read should read at least a byte. + assert!(bytes_read > 0); + assert_eq!(read_buffer[0..bytes_read], bytes[0..bytes_read]); + + let write_buffer = b"some additional bytes"; + let (buffer1, buffer2) = write_buffer.split_at(write_buffer.len() / 2); + + let bytes_written = f.write_vectored(&[IoSlice::new(buffer1), IoSlice::new(buffer2)]).unwrap(); + + // Vectored write should write at least a byte. + assert!(bytes_written > 0); + + // Reset file cursor to read the written bytes. + f.seek(SeekFrom::Start(bytes_read as u64)).unwrap(); + let mut written_bytes = vec![0u8; bytes_written]; + f.read_exact(&mut written_bytes).unwrap(); + assert_eq!(written_bytes.as_slice(), &write_buffer[0..bytes_written]); +} + +/// Test vectored reads and vectored writes with byte offsets. +/// +/// **Note**: We skip this test on Solaris and Android targets. This is +/// because Solaris doesn't have `preadv`/`pwritev`, and on Android the +/// standard library uses `syscall(...)` for vectored reads/writes with +/// offsets because older Android versions also didn't have `preadv`/`pwritev`. +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "android"))))] +fn test_preadv_pwritev() { + use std::os::unix::fs::FileExt; + + let bytes = b"hello world!"; + let path = utils::prepare_with_content("miri_test_fs_preadv_pwritev.txt", bytes); + let mut f = OpenOptions::new().read(true).write(true).open(path).unwrap(); + + const OFFSET: usize = 2; + + let mut read_buffer = [0u8; 10]; + let (buffer1, buffer2) = read_buffer.split_at_mut(5); + + let bytes_read = f + .read_vectored_at(&mut [IoSliceMut::new(buffer1), IoSliceMut::new(buffer2)], OFFSET as u64) + .unwrap(); + + // Vectored read should read at least a byte at the provided offset. + assert!(bytes_read > 0); + assert_eq!(read_buffer[0..bytes_read], bytes[OFFSET..(bytes_read + OFFSET)]); + + let write_buffer = b"some additional bytes"; + let (buffer1, buffer2) = write_buffer.split_at(write_buffer.len() / 2); + + let bytes_written = f + .write_vectored_at( + &[IoSlice::new(buffer1), IoSlice::new(buffer2)], + (bytes.len() + OFFSET) as u64, + ) + .unwrap(); + + // Vectored write should write at least a byte at the provided offset. + assert!(bytes_written > 0); + + // Reset file cursor to read the written bytes. We move the cursor + // to include the offset. + f.seek(SeekFrom::Start((bytes.len() + OFFSET) as u64)).unwrap(); + let mut written_bytes = vec![0u8; bytes_written]; + f.read_exact(&mut written_bytes).unwrap(); + assert_eq!(written_bytes.as_slice(), &write_buffer[0..bytes_written]); +} diff --git a/src/tools/miri/tests/pass/shims/socket.rs b/src/tools/miri/tests/pass/shims/socket.rs index 4f448fa44780b..335df02b54f82 100644 --- a/src/tools/miri/tests/pass/shims/socket.rs +++ b/src/tools/miri/tests/pass/shims/socket.rs @@ -16,6 +16,7 @@ fn main() { test_peer_addr(); test_shutdown(); test_sockopt_ttl(); + test_sockopt_nodelay(); } fn test_create_ipv4_listener() { @@ -26,21 +27,15 @@ fn test_create_ipv6_listener() { let _listener_ipv6 = TcpListener::bind("[::1]:0").unwrap(); } -/// Try to connect to a TCP listener running in a separate thread and -/// accepting connections. +/// Try to connect to a TCP listener and accepting connections. fn test_accept_and_connect() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); // Get local address with randomized port to know where // we need to connect to. let address = listener.local_addr().unwrap(); - let handle = thread::spawn(move || { - let (_stream, _addr) = listener.accept().unwrap(); - }); - let _stream = TcpStream::connect(address).unwrap(); - - handle.join().unwrap(); + let (_other_stream, _addr) = listener.accept().unwrap(); } /// Test reading and writing into two connected sockets and ensuring @@ -119,8 +114,6 @@ fn test_peer_addr() { /// Test shutting down TCP streams. fn test_shutdown() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - // Get local address with randomized port to know where - // we need to connect to. let address = listener.local_addr().unwrap(); // Start server thread. @@ -157,7 +150,20 @@ fn test_shutdown() { /// Test setting and reading the TTL socket option. fn test_sockopt_ttl() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - listener.ttl().unwrap(); + listener.set_ttl(16).unwrap(); + assert_eq!(listener.ttl().unwrap(), 16); +} + +/// Test setting and reading the TCP nodelay socket option. +fn test_sockopt_nodelay() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + + let stream = TcpStream::connect(address).unwrap(); + let _other_end = listener.accept().unwrap(); - // TODO: Once we support setting the TTL we should also test it here. + stream.set_nodelay(true).unwrap(); + assert_eq!(stream.nodelay().unwrap(), true); + stream.set_nodelay(false).unwrap(); + assert_eq!(stream.nodelay().unwrap(), false); } diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr index e09aed2cf5d01..6edb1f91c90c2 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── |?Cel | ├──── |?Cel | └──── @@ -9,8 +9,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Cel | ├──── | Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr index e09aed2cf5d01..6edb1f91c90c2 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── |?Cel | ├──── |?Cel | └──── @@ -9,8 +9,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Cel | ├──── | Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr index 5dbfff718b1e6..3a7c55a8dab43 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | ReIM| └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr index 5dbfff718b1e6..3a7c55a8dab43 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | ReIM| └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr index 1d939329040fa..c7f7397593d33 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr @@ -1,6 +1,6 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── +| Unq | └─┬── |?Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr index 1d939329040fa..c7f7397593d33 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr @@ -1,6 +1,6 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── +| Unq | └─┬── |?Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr index 4d77d96776d31..74abcef5870b1 100644 --- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr @@ -1,14 +1,14 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | └─┬── | Res | └─┬── @@ -17,7 +17,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├─┬── | Res | │ └─┬── @@ -27,10 +27,10 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Dis | ├─┬── | Dis | │ └─┬── | Dis | │ └──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr index d33bfcdc0097a..7ab96e3f378d9 100644 --- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr @@ -1,24 +1,24 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └─┬── -| Act | └─┬── -| Act | └──── Strongly protected +| Unq | └─┬── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── Strongly protected ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Frz | ├─┬── | Frz | │ └─┬── | Frz | │ └──── @@ -27,10 +27,10 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Dis | ├─┬── | Dis | │ └─┬── | Dis | │ └──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/formatting.stderr b/src/tools/miri/tests/pass/tree_borrows/formatting.stderr index 29f99034bab5c..5e4cc7be56bc0 100644 --- a/src/tools/miri/tests/pass/tree_borrows/formatting.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/formatting.stderr @@ -1,17 +1,17 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1.. 2.. 10.. 11.. 100.. 101..1000..1001..1024 -| Act | Act | Act | Act | Act | Act | Act | Act | Act | └─┬── -| Res | Act | Res | Act | Res | Act | Res | Act | Res | └─┬── -|-----| Act |-----|?Dis |-----|?Dis |-----|?Dis |-----| ├──── -|-----|-----|-----| Act |-----|?Dis |-----|?Dis |-----| ├──── +| Unq | Unq | Unq | Unq | Unq | Unq | Unq | Unq | Unq | └─┬── +| Res | Unq | Res | Unq | Res | Unq | Res | Unq | Res | └─┬── +|-----| Unq |-----|?Dis |-----|?Dis |-----|?Dis |-----| ├──── +|-----|-----|-----| Unq |-----|?Dis |-----|?Dis |-----| ├──── |-----|-----|-----|-----|-----| Frz |-----|?Dis |-----| ├──── -|-----|-----|-----|-----|-----|-----|-----| Act |-----| └──── +|-----|-----|-----|-----|-----|-----|-----| Unq |-----| └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Frz | └─┬── | Frz | ├─┬── | Frz | │ ├──── diff --git a/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr index 1dd871530caad..faccba6b0eead 100644 --- a/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────── 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └─┬── -| Act | └──── Strongly protected +| Unq | └─┬── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── Strongly protected ────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr index d589a06211182..a1f485208dfa9 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr @@ -1,15 +1,15 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └──── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Frz | ├──── | Res | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr index d589a06211182..a1f485208dfa9 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr @@ -1,15 +1,15 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └──── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Frz | ├──── | Res | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.stderr b/src/tools/miri/tests/pass/tree_borrows/reserved.stderr index be90382640b4a..ab173919abf5a 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reserved.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reserved.stderr @@ -2,7 +2,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── | ReIM| ├─┬── | ReIM| │ └─┬── @@ -13,7 +13,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── | ReIM| ├──── | ReIM| └──── @@ -22,16 +22,16 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | ReIM| ├──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── [protected] Foreign Read: Res -> Frz ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├─┬── | Res | │ └─┬── @@ -42,7 +42,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├──── | Res | └──── @@ -51,8 +51,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Dis | ├──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr index 583c84534395c..d671f31d5a802 100644 --- a/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Frz | └─┬── | Frz | ├──── | Frz | └──── (exposed) diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 96c0ccb7d6974..1449013e0478e 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -1,5 +1,4 @@ use std::env; -use std::ffi::OsString; use std::num::NonZero; use std::path::{Path, PathBuf}; use std::process::Command; @@ -330,13 +329,9 @@ fn main() -> Result<()> { let target = get_target(&host); let tmpdir = tempfile::Builder::new().prefix("miri-uitest-").tempdir()?; - let mut args = std::env::args_os(); - - // Skip the program name and check whether this is a `./miri run-dep` invocation - if let Some(first) = args.nth(1) - && first == "--miri-run-dep-mode" - { - return run_dep_mode(target, args); + // Check whether this is a `./miri run` invocation + if let Ok(mode) = env::var("MIRI_RUN_MODE") { + return run_mode(target, mode); } ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; @@ -366,21 +361,67 @@ fn main() -> Result<()> { Ok(()) } -fn run_dep_mode(target: String, args: impl Iterator) -> Result<()> { +fn run_mode(target: String, mode: String) -> Result<()> { + let native = mode == "native"; + let mut config = miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice + if native { + // Patch things up so that we actually compile the program. + config.program.envs.push(("MIRI_BE_RUSTC".into(), Some("host".into()))); + config.comment_defaults.base().set_custom( + "dependencies", + DependencyBuilder { + crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), + ..Default::default() + }, + ); + } config.fill_host_and_target()?; - let dep_builder = BuildManager::one_off(config.clone()); - // Only set these for the actual run, not the dep builder, so invalid flags do not fail - // the dependency build. - config.program.args = args.collect(); - let test_config = TestConfig::one_off_runner(config, PathBuf::new()); + // Reset `args` (otherwise we'll get JSON output). + config.program.args = vec![]; + // Compute the actual Miri invocation command. + let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new()); let mut cmd = test_config.config.program.build(&test_config.config.out_dir); - cmd.arg("--target").arg(test_config.config.target.as_ref().unwrap()); - // Build dependencies - test_config.apply_custom(&mut cmd, &dep_builder).expect("failed to build dependencies"); + // For some reason we need to set the target ourselves. + cmd.arg("--target").arg(&target); + // Also forward arguments to the program (skipping the binary name). + // We don't put this in the `config` since we don't want it to affect the dependency build. + cmd.args({ + let mut args = env::args_os(); + args.next().unwrap(); + args + }); + + // Build dependencies (which will mutate that command) + test_config + .apply_custom(&mut cmd, &BuildManager::one_off(config.clone())) + .expect("failed to build dependencies"); + // Finally, actually run Miri. + let exit_status = cmd.spawn()?.wait()?; + if !exit_status.success() { + std::process::exit(1) + } - if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } + if native { + // We just built the program, we still have to run it. We can't use the ui_test `Run` flag + // as that needs an actual BuildManager, not just the one-off stub we have here. So we + // implement the core logic ourselves. + + // First, figure out the output binary by re-running the compiler with `--print`. + cmd.arg("--print").arg("file-names"); + let output = cmd.output()?; + let exe = std::str::from_utf8(&output.stdout).unwrap().trim(); + let exe = config.out_dir.join(exe); + // Then run that binary. + let mut cmd = Command::new(exe); + let exit_status = cmd.spawn()?.wait()?; + if !exit_status.success() { + std::process::exit(1) + } + } + + Ok(()) } diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index e9cde20412f49..3cc4610c6b0a3 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -172,3 +172,38 @@ extern "Rust" { /// As far as Miri is concerned, this is equivalent to `yield_now`. pub fn miri_spin_loop(); } + +// Stubs so we can run things without Miri. +#[cfg(not(miri))] +mod stubs { + use std::ffi::CStr; + use std::io::Write; + + pub unsafe fn miri_write_to_stdout(bytes: &[u8]) { + std::io::stdout().write_all(bytes).unwrap() + } + + pub unsafe fn miri_write_to_stderr(bytes: &[u8]) { + std::io::stderr().write_all(bytes).unwrap() + } + + pub unsafe fn miri_host_to_target_path( + path: *const core::ffi::c_char, + out: *mut core::ffi::c_char, + out_size: usize, + ) -> usize { + let path = CStr::from_ptr(path); + let len = path.count_bytes() + 1; + if out_size >= len { + out.copy_from(path.as_ptr(), len); + 0 + } else { + len + } + } + + pub unsafe fn miri_run_provenance_gc() {} +} + +#[cfg(not(miri))] +pub use stubs::*; diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 89a581c90aa88..36b3705027aa1 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -43,15 +43,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -78,15 +78,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bincode" @@ -114,9 +114,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "block-buffer" @@ -127,17 +127,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "cc" -version = "1.2.45" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -151,9 +160,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -164,9 +173,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -174,9 +183,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -186,9 +195,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -198,15 +207,21 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" [[package]] name = "core-foundation-sys" @@ -223,6 +238,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -234,14 +258,23 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "darling" version = "0.20.11" @@ -330,8 +363,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid", + "crypto-common 0.2.2", ] [[package]] @@ -342,9 +386,9 @@ checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "ego-tree" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" +checksum = "b04dc5a38e4f151a79d9f2451ae6037fb6eaf5cba34771f44781f80e508498e3" [[package]] name = "elasticlunr-rs" @@ -395,21 +439,21 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -421,27 +465,47 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "font-awesome-as-a-crate" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc" +checksum = "b40fbe89eb7639971503bf5f17bd31c41338e8f92e03c5744d07f2e03d43f679" [[package]] -name = "futf" -version = "0.1.5" +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ - "mac", - "new_debug_unreachable", + "futures-core", + "futures-task", + "pin-project-lite", + "slab", ] [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -458,14 +522,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", + "wasip3", ] [[package]] @@ -480,9 +545,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.3.2" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" +checksum = "d43ccdfe15a81ab0a8af639e90254227c9a46afd9c5f5b6ec7efaa345c4b0f00" dependencies = [ "derive_builder", "log", @@ -491,14 +556,23 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -514,9 +588,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "html5ever" -version = "0.36.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" dependencies = [ "log", "markup5ever", @@ -537,11 +611,20 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -561,6 +644,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -569,12 +658,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] @@ -585,16 +676,18 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -605,11 +698,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.177" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linereader" @@ -622,9 +721,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "lock_api" @@ -637,21 +736,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "mac" -version = "0.1.1" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "markup5ever" -version = "0.36.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", "tendril", @@ -669,23 +762,23 @@ dependencies = [ [[package]] name = "mdbook-core" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a3873d4afac65583f1acb56ff058df989d5b4a2464bb02c785549727d307ee" +checksum = "6fc1c4da7fd9e2e412f3891428f9468fab890ed159723ed0892bb85a049ac1c1" dependencies = [ "anyhow", "regex", "serde", "serde_json", - "toml 0.9.8", + "toml 1.1.2+spec-1.1.0", "tracing", ] [[package]] name = "mdbook-driver" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a229930b29a9908560883e1f386eae25d8a971d259a80f49916a50627f04a42d" +checksum = "2068566fc3c100cfd19f4a13e4e0eb4fdcd2250cfd0aa633ec25102b1c98d53e" dependencies = [ "anyhow", "indexmap", @@ -700,16 +793,16 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "toml 0.9.8", + "toml 1.1.2+spec-1.1.0", "topological-sort", "tracing", ] [[package]] name = "mdbook-html" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee80c03c65e3212fb528b8c9be5568a6a85cf795d03cf9fd6ba39ad52069ca" +checksum = "85ed2140251689f928615f0a5615413eb072eb28e003d179257aa4f034c95513" dependencies = [ "anyhow", "ego-tree", @@ -722,11 +815,11 @@ dependencies = [ "mdbook-core", "mdbook-markdown", "mdbook-renderer", - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "regex", "serde", "serde_json", - "sha2", + "sha2 0.11.0", "tracing", ] @@ -742,7 +835,7 @@ dependencies = [ "mdbook-preprocessor", "mdbook-renderer", "polib", - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "pulldown-cmark-to-cmark 21.1.0", "regex", "semver", @@ -753,20 +846,20 @@ dependencies = [ [[package]] name = "mdbook-markdown" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c41bf35212f5d8b83e543aa6a4887dc5709c8489c5fb9ed00f1b51ce1a2cc6" +checksum = "2cb3ca9eadf02ce206118a0b9c264718723d669b110c01a720fa9a0786f30642" dependencies = [ - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "regex", "tracing", ] [[package]] name = "mdbook-preprocessor" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d87bf40be0597f26f0822f939a64f02bf92c4655ba04490aadbf83601a013bb" +checksum = "8eff7b4afaafd664a649a03b8891ad8199aef359a35b911ad6c31acab38ed209" dependencies = [ "anyhow", "mdbook-core", @@ -776,9 +869,9 @@ dependencies = [ [[package]] name = "mdbook-renderer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ed59f225b3ae4283c56bea633db83184627a090d892908bd66990c68e10b43" +checksum = "e607259410d53aa5cdaf5b6c1c6b3fd61f2e0f0523ebf457d34cd4f0b71e2a88" dependencies = [ "anyhow", "mdbook-core", @@ -807,14 +900,14 @@ dependencies = [ [[package]] name = "mdbook-summary" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d85b291d67a69c92e939450390fe34d6ea418a868c8f7b42f0b300af35a7b" +checksum = "cae8a734e5e35b0bc145b46d01fd27e266ba647dcdb9b8c907cb6a29eca9122b" dependencies = [ "anyhow", "mdbook-core", "memchr", - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "serde", "tracing", ] @@ -837,9 +930,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miniz_oxide" @@ -892,9 +985,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -933,9 +1026,9 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pest" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -943,9 +1036,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -953,9 +1046,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", @@ -966,12 +1059,12 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -1015,9 +1108,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "polib" @@ -1034,11 +1127,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1058,9 +1161,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ "bitflags", "memchr", @@ -1089,29 +1192,29 @@ version = "21.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8246feae3db61428fd0bb94285c690b460e4517d83152377543ca802357785f1" dependencies = [ - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "railroad" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5b8e8a7c20c600f9b98cbf46b64e63d5c9e69deb98cee1ff264de9f1dda5d" +checksum = "813b53dbe6d583f1ac0c3fb394e0ce3b613530adff8265c274fcdfcd82cc6da5" dependencies = [ "unicode-width", ] @@ -1127,9 +1230,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1139,9 +1242,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1150,9 +1253,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustbook" @@ -1168,9 +1271,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -1185,12 +1288,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -1208,9 +1305,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1244,15 +1341,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1266,9 +1363,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -1280,8 +1377,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -1301,15 +1409,21 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -1327,7 +1441,6 @@ dependencies = [ "parking_lot", "phf_shared", "precomputed-hash", - "serde", ] [[package]] @@ -1350,9 +1463,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1373,15 +1486,15 @@ dependencies = [ "regex-syntax", "serde", "serde_derive", - "thiserror 2.0.17", + "thiserror 2.0.18", "walkdir", ] [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", @@ -1392,12 +1505,11 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ - "futf", - "mac", + "new_debug_unreachable", "utf-8", ] @@ -1418,11 +1530,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -1438,9 +1550,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1470,17 +1582,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.3", - "toml_datetime 0.7.3", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 1.0.3", ] [[package]] @@ -1494,9 +1606,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -1512,16 +1624,16 @@ dependencies = [ "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] @@ -1532,9 +1644,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "topological-sort" @@ -1544,9 +1656,9 @@ checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1566,9 +1678,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -1587,9 +1699,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -1605,9 +1717,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -1617,15 +1729,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" @@ -1633,6 +1745,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf-8" version = "0.7.6" @@ -1669,18 +1787,27 @@ dependencies = [ [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -1691,9 +1818,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1701,9 +1828,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -1714,18 +1841,52 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web_atoms" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd0c322f146d0f8aad130ce6c187953889359584497dac6561204c8e17bb43d" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" dependencies = [ "phf", "phf_codegen", @@ -1812,15 +1973,115 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index 6f28ebe519314..f981d09fbbee1 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] clap = { version = "4.0.32", features = ["cargo"] } -mdbook-driver = { version = "0.5.2", features = ["search"] } +mdbook-driver = { version = "0.5.3", features = ["search"] } mdbook-i18n-helpers = "0.4.0" mdbook-spec = { path = "../../doc/reference/tools/mdbook-spec" } mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" } diff --git a/src/tools/rustfmt/CODE_OF_CONDUCT.md b/src/tools/rustfmt/CODE_OF_CONDUCT.md index 2acddfeefdf6a..e3708bc485399 100644 --- a/src/tools/rustfmt/CODE_OF_CONDUCT.md +++ b/src/tools/rustfmt/CODE_OF_CONDUCT.md @@ -1,40 +1,3 @@ # The Rust Code of Conduct -A version of this document [can be found online](https://www.rust-lang.org/conduct.html). - -## Conduct - -**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) - -* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. -* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. -* Please be kind and courteous. There's no need to be mean or rude. -* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. -* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. -* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. -* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. - -## Moderation - - -These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team]. - -1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) -2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. -3. Moderators will first respond to such remarks with a warning. -4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. -5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. -6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. -7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. -8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. - -In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. - -And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. - -The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. - -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* - -[mod_team]: https://www.rust-lang.org/team.html#Moderation-team +The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 32f71703e019f..8b749c46c1d58 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -960,6 +960,7 @@ fn format_impl_ref_and_type( if let Some(of_trait) = of_trait.as_deref() { result.push_str(format_defaultness(of_trait.defaultness)); + result.push_str(format_constness(*constness)); result.push_str(format_safety(of_trait.safety)); } else { result.push_str(format_constness(*constness)); @@ -980,7 +981,6 @@ fn format_impl_ref_and_type( let trait_ref_overhead; if let Some(of_trait) = of_trait.as_deref() { - result.push_str(format_constness_right(*constness)); let polarity_str = match of_trait.polarity { ast::ImplPolarity::Negative(_) => "!", ast::ImplPolarity::Positive => "", @@ -1889,15 +1889,16 @@ pub(crate) fn rewrite_struct_field_prefix( field: &ast::FieldDef, ) -> RewriteResult { let vis = format_visibility(context, &field.vis); + let mut_restriction = format_mut_restriction(context, &field.mut_restriction); let safety = format_safety(field.safety); let type_annotation_spacing = type_annotation_spacing(context.config); Ok(match field.ident { Some(name) => format!( - "{vis}{safety}{}{}:", + "{vis}{mut_restriction}{safety}{}{}:", rewrite_ident(context, name), type_annotation_spacing.0 ), - None => format!("{vis}{safety}"), + None => format!("{vis}{mut_restriction}{safety}"), }) } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index de72c9ce14bc3..1fcc59f2a1ea4 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use rustc_ast::YieldKind; use rustc_ast::ast::{ - self, Attribute, ImplRestriction, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, - RestrictionKind, Visibility, VisibilityKind, + self, Attribute, ImplRestriction, MetaItem, MetaItemInner, MetaItemKind, MutRestriction, + NodeId, Path, RestrictionKind, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol}; @@ -81,6 +81,13 @@ pub(crate) fn format_impl_restriction( format_restriction("impl", context, &impl_restriction.kind) } +pub(crate) fn format_mut_restriction( + context: &RewriteContext<'_>, + mut_restriction: &MutRestriction, +) -> String { + format_restriction("mut", context, &mut_restriction.kind) +} + fn format_restriction( kw: &'static str, context: &RewriteContext<'_>, @@ -125,14 +132,6 @@ pub(crate) fn format_constness(constness: ast::Const) -> &'static str { } } -#[inline] -pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str { - match constness { - ast::Const::Yes(..) => " const", - ast::Const::No => "", - } -} - #[inline] pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str { match defaultness { diff --git a/src/tools/rustfmt/tests/source/const_trait.rs b/src/tools/rustfmt/tests/source/const_trait.rs deleted file mode 100644 index 99414a74f250c..0000000000000 --- a/src/tools/rustfmt/tests/source/const_trait.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![feature(trait_alias, const_trait_impl)] - -const trait Bar {} - -const trait Foo = Bar; - -impl const Bar for () {} - -// const impl gets reformatted to impl const.. for now -const impl Bar for u8 {} - -struct X; - -const impl X {} diff --git a/src/tools/rustfmt/tests/source/mut-restriction.rs b/src/tools/rustfmt/tests/source/mut-restriction.rs new file mode 100644 index 0000000000000..990cbd476bfac --- /dev/null +++ b/src/tools/rustfmt/tests/source/mut-restriction.rs @@ -0,0 +1,108 @@ +#![feature(mut_restrictions, unsafe_fields)] + +struct FooS { + pub + mut(crate) + field1: (), + pub + mut(crate + ) + unsafe + field2: (), +} + +struct BazS { + pub + mut ( in foo + :: + bar ) + field1: (), + pub + mut( + in foo + :: + bar + ) unsafe + field2: (), +} + +struct FooS2( + pub + mut(crate) + (), +); + +struct BazS2( + pub + mut ( in foo + :: + bar ) + (), +); + +enum Enum { + Foo { + pub(crate) mut(self) + field1: (), + pub + mut(self + ) + unsafe + field2: (), + }, + Baz { + pub + mut ( in foo + :: + bar ) + field1: (), + pub( + crate + ) + mut( + in foo + :: + bar + ) unsafe field2: (), + }, + FooT( + pub(crate) + mut(self) + (), + ), + BazT( + pub(crate + ) + mut ( in foo + :: + bar ) + (), + ), + +} + +union Union { + pub + mut(crate) + field1: (), + pub(crate + ) + mut ( in foo + :: + bar ) + field2: (), + pub + mut(crate + ) + unsafe + field3: (), + pub( + crate + ) + mut( + in foo + :: + bar + ) unsafe + field4: (), +} diff --git a/src/tools/rustfmt/tests/target/const_trait.rs b/src/tools/rustfmt/tests/target/const_trait.rs index 337a4cf4140e8..bb6ba1cd85b1d 100644 --- a/src/tools/rustfmt/tests/target/const_trait.rs +++ b/src/tools/rustfmt/tests/target/const_trait.rs @@ -4,10 +4,7 @@ const trait Bar {} const trait Foo = Bar; -impl const Bar for () {} - -// const impl gets reformatted to impl const.. for now -impl const Bar for u8 {} +const impl Bar for () {} struct X; diff --git a/src/tools/rustfmt/tests/target/impls.rs b/src/tools/rustfmt/tests/target/impls.rs index 99e02990e4177..71dfb5591e7ce 100644 --- a/src/tools/rustfmt/tests/target/impls.rs +++ b/src/tools/rustfmt/tests/target/impls.rs @@ -244,7 +244,7 @@ where } // #4084 -impl const std::default::Default for Struct { +const impl std::default::Default for Struct { #[inline] fn default() -> Self { Self { f: 12.5 } diff --git a/src/tools/rustfmt/tests/target/mut-restriction.rs b/src/tools/rustfmt/tests/target/mut-restriction.rs new file mode 100644 index 0000000000000..3ac5d6ae912d1 --- /dev/null +++ b/src/tools/rustfmt/tests/target/mut-restriction.rs @@ -0,0 +1,35 @@ +#![feature(mut_restrictions, unsafe_fields)] + +struct FooS { + pub mut(crate) field1: (), + pub mut(crate) unsafe field2: (), +} + +struct BazS { + pub mut(in foo::bar) field1: (), + pub mut(in foo::bar) unsafe field2: (), +} + +struct FooS2(pub mut(crate) ()); + +struct BazS2(pub mut(in foo::bar) ()); + +enum Enum { + Foo { + pub(crate) mut(self) field1: (), + pub mut(self) unsafe field2: (), + }, + Baz { + pub mut(in foo::bar) field1: (), + pub(crate) mut(in foo::bar) unsafe field2: (), + }, + FooT(pub(crate) mut(self) ()), + BazT(pub(crate) mut(in foo::bar) ()), +} + +union Union { + pub mut(crate) field1: (), + pub(crate) mut(in foo::bar) field2: (), + pub mut(crate) unsafe field3: (), + pub(crate) mut(in foo::bar) unsafe field4: (), +} diff --git a/src/tools/rustfmt/tests/target/type.rs b/src/tools/rustfmt/tests/target/type.rs index 623192b72b8b7..4855f726ea3f9 100644 --- a/src/tools/rustfmt/tests/target/type.rs +++ b/src/tools/rustfmt/tests/target/type.rs @@ -153,7 +153,7 @@ const fn not_quite_const() -> i32 { ::CONST } -impl const T for U {} +const impl T for U {} fn apit(_: impl [const] T) {} diff --git a/src/version b/src/version index acbb747ac540f..783fda86436c2 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.97.0 +1.98.0 diff --git a/tests/codegen-llvm/array-equality.rs b/tests/codegen-llvm/array-equality.rs index 8e4c170e4e674..385b7d7803594 100644 --- a/tests/codegen-llvm/array-equality.rs +++ b/tests/codegen-llvm/array-equality.rs @@ -9,8 +9,8 @@ #[no_mangle] pub fn array_eq_value(a: [u16; 3], b: [u16; 3]) -> bool { // CHECK-NEXT: start: - // CHECK-NEXT: %2 = icmp eq i48 %0, %1 - // CHECK-NEXT: ret i1 %2 + // CHECK-NEXT: %_0 = icmp eq i48 %0, %1 + // CHECK-NEXT: ret i1 %_0 a == b } diff --git a/tests/codegen-llvm/atomicptr.rs b/tests/codegen-llvm/atomicptr.rs index 9d5e618fe76f2..9042f71e9442c 100644 --- a/tests/codegen-llvm/atomicptr.rs +++ b/tests/codegen-llvm/atomicptr.rs @@ -19,17 +19,20 @@ pub fn helper(_: usize) {} // CHECK-LABEL: @atomicptr_fetch_byte_add #[no_mangle] pub fn atomicptr_fetch_byte_add(a: &AtomicPtr, v: usize) -> *mut u8 { - // CHECK: llvm.lifetime.start + // CHECK: start // CHECK-NEXT: %[[RET:.*]] = atomicrmw add ptr %{{.*}}, [[USIZE]] %v - // CHECK-NEXT: inttoptr [[USIZE]] %[[RET]] to ptr + // CHECK-NEXT: %[[RETPTR:.*]] = inttoptr [[USIZE]] %[[RET]] to ptr + // CHECK-NEXT: ret ptr %[[RETPTR]] a.fetch_byte_add(v, Relaxed) } // CHECK-LABEL: @atomicptr_swap #[no_mangle] pub fn atomicptr_swap(a: &AtomicPtr, ptr: *mut u8) -> *mut u8 { + // CHECK: start // CHECK-NOT: ptrtoint - // CHECK: atomicrmw xchg ptr %{{.*}}, ptr %{{.*}} monotonic + // CHECK-NEXT: %[[RET:.*]] = atomicrmw xchg ptr %{{.*}}, ptr %{{.*}} monotonic // CHECK-NOT: inttoptr + // CHECK-NEXT: ret ptr %[[RET]] a.swap(ptr, Relaxed) } diff --git a/tests/codegen-llvm/call-site-inline-attributes.rs b/tests/codegen-llvm/call-site-inline-attributes.rs new file mode 100644 index 0000000000000..01839526c50c1 --- /dev/null +++ b/tests/codegen-llvm/call-site-inline-attributes.rs @@ -0,0 +1,40 @@ +//@ compile-flags: -O -Zinline-mir=no -Cno-prepopulate-passes -Zmerge-functions=disabled + +#![crate_type = "lib"] + +// This test checks that we add inlinehint for #[inline], noinline for #[inline(never)], and +// alwaysinline for #[inline(always)] to call sites. + +#[unsafe(no_mangle)] +fn calls_something_noinline() { + // CHECK-LABEL @calls_something_noinline + // CHECK: call void @{{.*}}noinline_fn() #[[NOINLINE:[0-9]+]] + noinline_fn(); +} + +#[inline(never)] +fn noinline_fn() {} + +#[unsafe(no_mangle)] +fn calls_something_inline() { + // CHECK-LABEL @calls_something_inlinehint + // CHECK: call void @{{.*}}inlinehint_fn() #[[INLINEHINT:[0-9]+]] + inlinehint_fn(); +} + +#[inline] +fn inlinehint_fn() {} + +#[unsafe(no_mangle)] +fn calls_something_alwaysinline() { + // CHECK-LABEL @calls_something_alwaysinline + // CHECK: call void @{{.*}}alwaysinline_fn() #[[ALWAYSINLINE:[0-9]+]] + alwaysinline_fn(); +} + +#[inline(always)] +fn alwaysinline_fn() {} + +//CHECK: attributes #[[NOINLINE]] = {{{.*}} noinline {{.*}}} +//CHECK: attributes #[[INLINEHINT]] = {{{.*}} inlinehint {{.*}}} +//CHECK: attributes #[[ALWAYSINLINE]] = {{{.*}} alwaysinline {{.*}}} diff --git a/tests/codegen-llvm/cffi/c-variadic-naked.rs b/tests/codegen-llvm/cffi/c-variadic-naked.rs index caca6d327dd63..a04d3efca9cd9 100644 --- a/tests/codegen-llvm/cffi/c-variadic-naked.rs +++ b/tests/codegen-llvm/cffi/c-variadic-naked.rs @@ -1,5 +1,5 @@ //@ needs-asm-support -//@ needs-asm-mnemonic: ret +//@ needs-asm-ret // tests that `va_start` is not injected into naked functions diff --git a/tests/codegen-llvm/dst-vtable-align-nonzero.rs b/tests/codegen-llvm/dst-vtable-align-nonzero.rs index 2eee91876683c..80bb55c705dd1 100644 --- a/tests/codegen-llvm/dst-vtable-align-nonzero.rs +++ b/tests/codegen-llvm/dst-vtable-align-nonzero.rs @@ -52,7 +52,7 @@ pub fn does_not_eliminate_runtime_check_when_align_2( // CHECK-LABEL: @align_load_from_align_of_val #[no_mangle] pub fn align_load_from_align_of_val(x: &dyn Trait) -> usize { - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] core::mem::align_of_val(x) } @@ -60,7 +60,7 @@ pub fn align_load_from_align_of_val(x: &dyn Trait) -> usize { #[no_mangle] pub unsafe fn align_load_from_vtable_align_intrinsic(x: &dyn Trait) -> usize { let (data, vtable): (*const (), *const ()) = core::mem::transmute(x); - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] core::intrinsics::vtable_align(vtable) } diff --git a/tests/codegen-llvm/dst-vtable-size-range.rs b/tests/codegen-llvm/dst-vtable-size-range.rs index 670f5e8d553fa..92fd68ece6a08 100644 --- a/tests/codegen-llvm/dst-vtable-size-range.rs +++ b/tests/codegen-llvm/dst-vtable-size-range.rs @@ -20,7 +20,7 @@ pub fn generate_exclusive_bound() -> usize { // CHECK-LABEL: @size_load_from_size_of_val #[no_mangle] pub fn size_load_from_size_of_val(x: &dyn Trait) -> usize { - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META:![0-9]+]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META:![0-9]+]] core::mem::size_of_val(x) } @@ -28,7 +28,7 @@ pub fn size_load_from_size_of_val(x: &dyn Trait) -> usize { #[no_mangle] pub unsafe fn size_load_from_vtable_size_intrinsic(x: &dyn Trait) -> usize { let (data, vtable): (*const (), *const ()) = core::mem::transmute(x); - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] core::intrinsics::vtable_size(vtable) } diff --git a/tests/codegen-llvm/inline-always-callsite.rs b/tests/codegen-llvm/inline-always-callsite.rs deleted file mode 100644 index ad133391ce267..0000000000000 --- a/tests/codegen-llvm/inline-always-callsite.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@ add-minicore -//@ compile-flags: --target aarch64-unknown-linux-gnu -Zinline-mir=no -C no-prepopulate-passes -//@ needs-llvm-components: aarch64 - -#![crate_type = "lib"] -#![feature(no_core, lang_items, target_feature_inline_always)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[inline(always)] -#[target_feature(enable = "neon")] -#[no_mangle] -pub fn single_target_feature() -> i32 { - 42 -} - -#[inline(always)] -#[target_feature(enable = "neon,i8mm")] -#[no_mangle] -// CHECK: define{{( noundef)?}} i32 @multiple_target_features() unnamed_addr #1 { -pub fn multiple_target_features() -> i32 { - // CHECK: %_0 = call{{( noundef)?}} i32 @single_target_feature() #3 - single_target_feature() -} - -#[no_mangle] -// CHECK: define{{( noundef)?}} i32 @inherits_from_global() unnamed_addr #2 { -pub fn inherits_from_global() -> i32 { - unsafe { - // CHECK: %_0 = call{{( noundef)?}} i32 @single_target_feature() #3 - single_target_feature() - } -} - -// Attribute #3 requires the alwaysinline attribute, the alwaysinline attribute is not emitted on a -// function definition when target features are present, rather it will be moved onto the function -// call, if the features match up. -// -// CHECK: attributes #3 = { alwaysinline nounwind } diff --git a/tests/codegen-llvm/intrinsics/disjoint_bitor.rs b/tests/codegen-llvm/intrinsics/disjoint_bitor.rs index fc45439ee0b95..b9d17bc9281d0 100644 --- a/tests/codegen-llvm/intrinsics/disjoint_bitor.rs +++ b/tests/codegen-llvm/intrinsics/disjoint_bitor.rs @@ -8,14 +8,16 @@ use std::intrinsics::disjoint_bitor; // CHECK-LABEL: @disjoint_bitor_signed #[no_mangle] pub unsafe fn disjoint_bitor_signed(x: i32, y: i32) -> i32 { - // CHECK: or disjoint i32 %x, %y + // CHECK: [[TEMP:%.+]] = or disjoint i32 %x, %y + // CHECK: ret i32 [[TEMP]] disjoint_bitor(x, y) } // CHECK-LABEL: @disjoint_bitor_unsigned #[no_mangle] pub unsafe fn disjoint_bitor_unsigned(x: u64, y: u64) -> u64 { - // CHECK: or disjoint i64 %x, %y + // CHECK: [[TEMP:%.+]] = or disjoint i64 %x, %y + // CHECK: ret i64 [[TEMP]] disjoint_bitor(x, y) } @@ -25,6 +27,6 @@ pub unsafe fn disjoint_bitor_literal() -> u8 { // This is a separate check because even without any passes, // LLVM will fold so it's not an instruction, which can assert in LLVM. - // CHECK: store i8 3 + // CHECK: ret i8 3 disjoint_bitor(1, 2) } diff --git a/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs b/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs new file mode 100644 index 0000000000000..2ae45aec5a623 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes -Z mir-opt-level=0 +//@ only-64bit (so I don't need to worry about usize) + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +// Here to have unit tests of what they actually emit and to track things like +// + +use std::intrinsics::{align_of_val, size_of_val}; + +// CHECK-LABEL: @align_of_array( +#[no_mangle] +pub unsafe fn align_of_array(x: &[u16; 7]) -> usize { + // CHECK: start: + // CHECK-NEXT: ret i64 2 + align_of_val(x) +} + +// CHECK-LABEL: @size_of_array( +#[no_mangle] +pub unsafe fn size_of_array(x: &[u16; 7]) -> usize { + // CHECK: start: + // CHECK-NEXT: ret i64 14 + size_of_val(x) +} + +// CHECK-LABEL: @align_of_slice( +#[no_mangle] +pub unsafe fn align_of_slice(x: &[u16]) -> usize { + // CHECK: start: + // CHECK-NEXT: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 + // CHECK-NEXT: ret i64 2 + align_of_val(x) +} + +// CHECK-LABEL: @size_of_slice( +#[no_mangle] +pub unsafe fn size_of_slice(x: &[u16]) -> usize { + // CHECK: start: + // CHECK-NEXT: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 + // CHECK-NEXT: ret i64 [[SIZE]] + size_of_val(x) +} diff --git a/tests/codegen-llvm/naked-fn/aligned.rs b/tests/codegen-llvm/naked-fn/aligned.rs index 8c4ac57a7bf90..77e637701fc95 100644 --- a/tests/codegen-llvm/naked-fn/aligned.rs +++ b/tests/codegen-llvm/naked-fn/aligned.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 //@ needs-asm-support -//@ needs-asm-mnemonic: ret +//@ needs-asm-ret //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) #![crate_type = "lib"] diff --git a/tests/codegen-llvm/naked-fn/min-function-alignment.rs b/tests/codegen-llvm/naked-fn/min-function-alignment.rs index 2619f4ef476a7..059eed06b66bb 100644 --- a/tests/codegen-llvm/naked-fn/min-function-alignment.rs +++ b/tests/codegen-llvm/naked-fn/min-function-alignment.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmin-function-alignment=16 //@ needs-asm-support -//@ needs-asm-mnemonic: ret +//@ needs-asm-ret //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity diff --git a/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs index 7a990b29e6ca1..26bb364c53d07 100644 --- a/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs +++ b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs @@ -1,5 +1,3 @@ -// FIXME: The FileCheck directives in this test are unchecked and probably broken. -//@ skip-filecheck //@ only-aarch64 #![crate_type = "lib"] #![allow(incomplete_features, internal_features)] @@ -38,27 +36,29 @@ pub fn svdup_n_f32(op: f32) -> svfloat32_t { unsafe { _svdup_n_f32(op) } } -// CHECK: define { , } @svcreate2_f32( %x0, %x1) +// CHECK-LABEL: define { , } @svcreate2_f32( +// CHECK-SAME: %x0, %x1) #[no_mangle] #[target_feature(enable = "sve")] pub fn svcreate2_f32(x0: svfloat32_t, x1: svfloat32_t) -> svfloat32x2_t { - // CHECK: %1 = insertvalue { , } poison, %x0, 0 - // CHECK-NEXT: %2 = insertvalue { , } %1, %x1, 1 + // CHECK: [[TUP0:%.*]] = insertvalue { , } poison, %x0, 0 + // CHECK-NEXT: [[TUP1:%.*]] = insertvalue { , } [[TUP0]], %x1, 1 unsafe { std::intrinsics::simd::scalable::sve_tuple_create2(x0, x1) } } -// CHECK: define { , , } @svcreate3_f32( %x0, %x1, %x2) +// CHECK-LABEL: define { , , } @svcreate3_f32( +// CHECK-SAME: %x0, %x1, %x2) #[no_mangle] #[target_feature(enable = "sve")] pub fn svcreate3_f32(x0: svfloat32_t, x1: svfloat32_t, x2: svfloat32_t) -> svfloat32x3_t { - // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate3_f32 - // CHECK: %1 = insertvalue { , , } poison, %x0, 0 - // CHECK-NEXT: %2 = insertvalue { , , } %1, %x1, 1 - // CHECK-NEXT: %3 = insertvalue { , , } %2, %x2, 2 + // CHECK: [[TUP0:%.*]] = insertvalue { , , } poison, %x0, 0 + // CHECK-NEXT: [[TUP1:%.*]] = insertvalue { , , } [[TUP0]], %x1, 1 + // CHECK-NEXT: [[TUP2:%.*]] = insertvalue { , , } [[TUP1]], %x2, 2 unsafe { std::intrinsics::simd::scalable::sve_tuple_create3(x0, x1, x2) } } -// CHECK: define { , , , } @svcreate4_f32( %x0, %x1, %x2, %x3) +// CHECK-LABEL: define { , , , } @svcreate4_f32( +// CHECK-SAME: %x0, %x1, %x2, %x3) #[no_mangle] #[target_feature(enable = "sve")] pub fn svcreate4_f32( @@ -67,35 +67,36 @@ pub fn svcreate4_f32( x2: svfloat32_t, x3: svfloat32_t, ) -> svfloat32x4_t { - // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate4_f32 - // CHECK: %1 = insertvalue { , , , } poison, %x0, 0 - // CHECK-NEXT: %2 = insertvalue { , , , } %1, %x1, 1 - // CHECK-NEXT: %3 = insertvalue { , , , } %2, %x2, 2 - // CHECK-NEXT: %4 = insertvalue { , , , } %3, %x3, 3 + // CHECK: [[TUP0:%.*]] = insertvalue { , , , } poison, %x0, 0 + // CHECK-NEXT: [[TUP1:%.*]] = insertvalue { , , , } [[TUP0]], %x1, 1 + // CHECK-NEXT: [[TUP2:%.*]] = insertvalue { , , , } [[TUP1]], %x2, 2 + // CHECK-NEXT: [[TUP3:%.*]] = insertvalue { , , , } [[TUP2]], %x3, 3 unsafe { std::intrinsics::simd::scalable::sve_tuple_create4(x0, x1, x2, x3) } } -// CHECK: define @svget2_f32({ , } %tup) +// CHECK-LABEL: define @svget2_f32( +// CHECK-SAME: { , } %tup) #[no_mangle] #[target_feature(enable = "sve")] -pub fn svget2_f32(tup: svfloat32x2_t) -> svfloat32_t { - // CHECK: %1 = extractvalue { , } %tup, 0 - unsafe { std::intrinsics::simd::scalable::sve_tuple_get::<_, _, { IDX }>(tup) } +pub fn svget2_f32(tup: svfloat32x2_t) -> svfloat32_t { + // CHECK: [[X:%.*]] = extractvalue { , } %tup, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_get::<_, _, 0>(tup) } } -// CHECK: define { , } @svset2_f32({ , } %tup, %x) +// CHECK-LABEL: define { , } @svset2_f32( +// CHECK-SAME: { , } %tup, %x) #[no_mangle] #[target_feature(enable = "sve")] -pub fn svset2_f32(tup: svfloat32x2_t, x: svfloat32_t) -> svfloat32x2_t { - // CHECK: %1 = insertvalue { , } %tup, %x, 0 - unsafe { std::intrinsics::simd::scalable::sve_tuple_set::<_, _, { IDX }>(tup, x) } +pub fn svset2_f32(tup: svfloat32x2_t, x: svfloat32_t) -> svfloat32x2_t { + // CHECK: [[TUP:%.*]] = insertvalue { , } %tup, %x, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_set::<_, _, 0>(tup, x) } } -// This function exists only so there are calls to the generic functions +// This function exists only so there are calls to the intrinsics #[target_feature(enable = "sve")] pub fn test() { let x = svdup_n_f32(2f32); let tup = svcreate2_f32(x, x); - let x = svget2_f32::<0>(tup); - let tup = svset2_f32::<0>(tup, x); + let x = svget2_f32(tup); + let _tup = svset2_f32(tup, x); } diff --git a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs index 294262d81526f..4af0287fbc120 100644 --- a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs +++ b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs @@ -20,29 +20,29 @@ use std::intrinsics::simd::simd_bitmask; // CHECK-LABEL: @bitmask_int #[no_mangle] pub unsafe fn bitmask_int(x: i32x2) -> u8 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2 - // CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8 + // CHECK: %{{_?[0-9]+}} = zext i2 [[C]] to i8 simd_bitmask(x) } // CHECK-LABEL: @bitmask_uint #[no_mangle] pub unsafe fn bitmask_uint(x: u32x2) -> u8 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2 - // CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8 + // CHECK: %{{_?[0-9]+}} = zext i2 [[C]] to i8 simd_bitmask(x) } // CHECK-LABEL: @bitmask_int16 #[no_mangle] pub unsafe fn bitmask_int16(x: i8x16) -> u16 { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1|2}}, {{|splat \(i8 7\)}} + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|0|1}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> - // CHECK: %{{[0-9]+}} = bitcast <16 x i1> [[B]] to i16 + // CHECK: %{{_?[0-9]+}} = bitcast <16 x i1> [[B]] to i16 // CHECK-NOT: zext simd_bitmask(x) } diff --git a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs index 79f00a6ed6032..7521ba1fcb573 100644 --- a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs +++ b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs @@ -22,39 +22,39 @@ pub type mask8x16 = Simd; // CHECK-LABEL: @reduce_any_32x2 #[no_mangle] pub unsafe fn reduce_any_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_any(x) } // CHECK-LABEL: @reduce_all_32x2 #[no_mangle] pub unsafe fn reduce_all_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_all(x) } // CHECK-LABEL: @reduce_any_8x16 #[no_mangle] pub unsafe fn reduce_any_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|0}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v16i1(<16 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.or.v16i1(<16 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_any(x) } // CHECK-LABEL: @reduce_all_8x16 #[no_mangle] pub unsafe fn reduce_all_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|0}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v16i1(<16 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.and.v16i1(<16 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_all(x) } diff --git a/tests/codegen-llvm/simd/aggregate-simd.rs b/tests/codegen-llvm/simd/aggregate-simd.rs index 57a301d634c81..2a31287fd9aea 100644 --- a/tests/codegen-llvm/simd/aggregate-simd.rs +++ b/tests/codegen-llvm/simd/aggregate-simd.rs @@ -88,11 +88,9 @@ pub fn transparent_simd_aggregate(x: [u32; 4]) -> u32 { // CHECK-LABEL: transparent_simd_aggregate // CHECK-NOT: alloca - // CHECK: %[[RET:.+]] = alloca [4 x i8] - // CHECK-NOT: alloca // CHECK: %a = load <4 x i32>, ptr %x, align 4 // CHECK: %[[TEMP:.+]] = extractelement <4 x i32> %a, i32 1 - // CHECK: store i32 %[[TEMP]], ptr %[[RET]] + // CHECK: ret i32 %[[TEMP]] unsafe { let a = Simd(x); diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs index a27102da96923..31b439ba3f900 100644 --- a/tests/debuginfo/numeric-types.rs +++ b/tests/debuginfo/numeric-types.rs @@ -213,8 +213,9 @@ //@ lldb-command:v nz_i128 //@ lldb-check:[...] 55 { 0 = { 0 = 55 } } -//@ lldb-command:v nz_isize -//@ lldb-check:[...] 66 { 0 = { 0 = 66 } } +// FIXME(#156886): "error: Invalid type: Cannot determine size" +//(DISABLED) @ lldb-command:v nz_isize +//(DISABLED) @ lldb-check:[...] 66 { 0 = { 0 = 66 } } //@ lldb-command:v/d nz_u8 //@ lldb-check:[...] 77 { 0 = { 0 = 77 } } @@ -231,8 +232,9 @@ //@ lldb-command:v nz_u128 //@ lldb-check:[...] 111 { 0 = { 0 = 111 } } -//@ lldb-command:v nz_usize -//@ lldb-check:[...] 122 { 0 = { 0 = 122 } } +// FIXME(#156886): "error: Invalid type: Cannot determine size" +//(DISABLED) @ lldb-command:v nz_usize +//(DISABLED) @ lldb-check:[...] 122 { 0 = { 0 = 122 } } use std::num::*; use std::sync::atomic::*; diff --git a/tests/debuginfo/path.rs b/tests/debuginfo/path.rs index 27b518fd89751..2fffadd8510ce 100644 --- a/tests/debuginfo/path.rs +++ b/tests/debuginfo/path.rs @@ -8,12 +8,8 @@ //@ lldb-command:print pathbuf //@ lldb-check:[...] "/some/path" { inner = "/some/path" { inner = { inner = size=10 { [0] = '/' [1] = 's' [2] = 'o' [3] = 'm' [4] = 'e' [5] = '/' [6] = 'p' [7] = 'a' [8] = 't' [9] = 'h' } } } } -//@ lldb-command:po pathbuf -//@ lldb-check:"/some/path" //@ lldb-command:print path //@ lldb-check:[...] "/some/path" { data_ptr = [...] length = 10 } -//@ lldb-command:po path -//@ lldb-check:"/some/path" use std::path::Path; diff --git a/tests/mir-opt/build_correct_coerce.rs b/tests/mir-opt/build_correct_coerce.rs index 004e1309b0b26..0721a1fddf067 100644 --- a/tests/mir-opt/build_correct_coerce.rs +++ b/tests/mir-opt/build_correct_coerce.rs @@ -1,5 +1,3 @@ -//@ skip-filecheck - // Validate that we record the target for the `as` coercion as `for<'a> fn(&'a (), &'a ())`, // and not `for<'a, 'b>(&'a (), &'b ())`. We previously did the latter due to a bug in // the code that records adjustments in HIR typeck. @@ -8,5 +6,8 @@ fn foo<'a, 'b>(_: &'a (), _: &'b ()) {} // EMIT_MIR build_correct_coerce.main.built.after.mir fn main() { + // CHECK-LABEL: fn main( + // CHECK: let _1: for<'a> fn(&'a (), &'a ()); + // CHECK: _1 = foo as for<'a> fn(&'a (), &'a ()) let x = foo as for<'a> fn(&'a (), &'a ()); } diff --git a/tests/mir-opt/pre-codegen/derived_ord_debug.rs b/tests/mir-opt/pre-codegen/derived_ord_debug.rs index 82851509d666f..f37e7ac7f5614 100644 --- a/tests/mir-opt/pre-codegen/derived_ord_debug.rs +++ b/tests/mir-opt/pre-codegen/derived_ord_debug.rs @@ -10,8 +10,8 @@ pub struct MultiField(char, i16); // EMIT_MIR derived_ord_debug.{impl#1}-cmp.runtime-optimized.after.mir // CHECK-LABEL: partial_cmp(_1: &MultiField, _2: &MultiField) -> Option -// CHECK: = ::partial_cmp( -// CHECK: = ::partial_cmp( +// CHECK: = ::cmp( +// CHECK: = Option::::Some( // CHECK-LABEL: cmp(_1: &MultiField, _2: &MultiField) -> std::cmp::Ordering // CHECK: = ::cmp( diff --git a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir index 85f7e76eea8c6..e018894cf3f95 100644 --- a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir @@ -4,49 +4,14 @@ fn ::partial_cmp(_1: &MultiField, debug self => _1; debug other => _2; let mut _0: std::option::Option; - let _3: &char; - let _4: &char; - let mut _5: std::option::Option; - let mut _6: isize; - let mut _7: i8; - let _8: &i16; - let _9: &i16; - scope 1 { - debug cmp => _5; - } + let mut _3: std::cmp::Ordering; bb0: { - _3 = &((*_1).0: char); - _4 = &((*_2).0: char); - _5 = ::partial_cmp(copy _3, copy _4) -> [return: bb1, unwind unreachable]; + _3 = ::cmp(copy _1, copy _2) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = discriminant(_5); - switchInt(move _6) -> [1: bb2, 0: bb4, otherwise: bb6]; - } - - bb2: { - _7 = discriminant(((_5 as Some).0: std::cmp::Ordering)); - switchInt(move _7) -> [0: bb3, otherwise: bb4]; - } - - bb3: { - _8 = &((*_1).1: i16); - _9 = &((*_2).1: i16); - _0 = ::partial_cmp(copy _8, copy _9) -> [return: bb5, unwind unreachable]; - } - - bb4: { - _0 = copy _5; - goto -> bb5; - } - - bb5: { + _0 = Option::::Some(move _3); return; } - - bb6: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir index cca704662dd70..94c79b30e4377 100644 --- a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir @@ -4,49 +4,14 @@ fn ::partial_cmp(_1: &MultiField, debug self => _1; debug other => _2; let mut _0: std::option::Option; - let _3: &char; - let _4: &char; - let mut _5: std::option::Option; - let mut _6: isize; - let mut _7: i8; - let _8: &i16; - let _9: &i16; - scope 1 { - debug cmp => _5; - } + let mut _3: std::cmp::Ordering; bb0: { - _3 = &((*_1).0: char); - _4 = &((*_2).0: char); - _5 = ::partial_cmp(copy _3, copy _4) -> [return: bb1, unwind continue]; + _3 = ::cmp(copy _1, copy _2) -> [return: bb1, unwind continue]; } bb1: { - _6 = discriminant(_5); - switchInt(move _6) -> [1: bb2, 0: bb4, otherwise: bb6]; - } - - bb2: { - _7 = discriminant(((_5 as Some).0: std::cmp::Ordering)); - switchInt(move _7) -> [0: bb3, otherwise: bb4]; - } - - bb3: { - _8 = &((*_1).1: i16); - _9 = &((*_2).1: i16); - _0 = ::partial_cmp(copy _8, copy _9) -> [return: bb5, unwind continue]; - } - - bb4: { - _0 = copy _5; - goto -> bb5; - } - - bb5: { + _0 = Option::::Some(move _3); return; } - - bb6: { - unreachable; - } } diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation-inline-attribute.pp index 125ed1c298262..fc409aa636d5c 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation-inline-attribute.pp @@ -38,33 +38,34 @@ fn foo(self: _) -> _ { - { - // Check that #[inline(hint)] is added to foo0 reuse inside another reuse + Trait::foo( + // Check that #[inline(hint)] is added to foo0 reuse inside another reuse + + // Check that #[inline(hint)] is added when other attributes present in inner reuse + + // Check that #[inline(never)] is preserved in inner reuse + + // Check that #[inline(always)] is preserved in inner reuse + + // Check that #[inline(never)] is preserved when there are other attributes in inner reuse + { #[attr = Inline(Hint)] fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) } - - // Check that #[inline(hint)] is added when other attributes present in inner reuse #[attr = Cold] #[attr = MustUse] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] #[attr = Inline(Hint)] fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) } - - // Check that #[inline(never)] is preserved in inner reuse #[attr = Inline(Never)] fn foo2(arg0: _) -> _ { to_reuse::foo(self / 2) } - - // Check that #[inline(always)] is preserved in inner reuse #[attr = Inline(Always)] fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) } - - // Check that #[inline(never)] is preserved when there are other attributes in inner reuse #[attr = Cold] #[attr = MustUse] #[attr = Inline(Never)] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) } - }.foo() + }) } // Check that #[inline(hint)] is added when there are other attributes present in trait reuse @@ -72,22 +73,22 @@ #[attr = MustUse] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] #[attr = Inline(Hint)] - fn foo1(self: _) -> _ { self.0.foo1() } + fn foo1(self: _) -> _ { Trait::foo1(self.0) } // Check that #[inline(never)] is preserved in trait reuse #[attr = Inline(Never)] - fn foo2(self: _) -> _ { self.0.foo2() } + fn foo2(self: _) -> _ { Trait::foo2(self.0) } // Check that #[inline(always)] is preserved in trait reuse #[attr = Inline(Always)] - fn foo3(self: _) -> _ { self.0.foo3() } + fn foo3(self: _) -> _ { Trait::foo3(self.0) } // Check that #[inline(never)] is preserved when there are other attributes in trait reuse #[attr = Cold] #[attr = MustUse] #[attr = Inline(Never)] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] - fn foo4(self: _) -> _ { self.0.foo4() } + fn foo4(self: _) -> _ { Trait::foo4(self.0) } } fn main() { } diff --git a/tests/pretty/delegation-self-rename.pp b/tests/pretty/delegation-self-rename.pp index 59a07315185c7..7f7afc403607b 100644 --- a/tests/pretty/delegation-self-rename.pp +++ b/tests/pretty/delegation-self-rename.pp @@ -19,7 +19,7 @@ #[attr = Inline(Hint)] fn foo<'a, Self, A, const B: _, const B2: _, T, U, impl FnOnce() -> usize>(self: _, arg1: _) -> _ where - 'a:'a { self.foo::(arg1) } + 'a:'a { Trait::<'a, A, B>::foo::(self, arg1) } #[attr = Inline(Hint)] fn bar usize>(self: _, arg1: _) -> _ { Trait::<'static, (), true>::foo::(self, arg1) } diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs index 8e4c26fc34508..c726f7ce04da7 100644 --- a/tests/run-make/naked-dead-code-elimination/rmake.rs +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -1,6 +1,6 @@ //@ ignore-cross-compile //@ needs-asm-support -//@ needs-asm-mnemonic: RET +//@ needs-asm-ret use run_make_support::symbols::object_contains_any_symbol; use run_make_support::{bin_name, rustc}; diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 7202ed71a2a82..0a2da1099b820 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -125,6 +125,8 @@ Options: Set the most restrictive lint level. More restrictive lints are capped at this level. By default, it is at `forbid` level. + --remap-path-prefix FROM=TO + Remap source names in compiler messages --index-page PATH Markdown file to be used as index page --enable-index-page @@ -158,8 +160,6 @@ Options: Force all doctests to be compiled as a single binary, instead of one binary per test. If merging fails, rustdoc will emit a hard error. - --remap-path-prefix FROM=TO - Remap source names in compiler messages --remap-path-scope [macro,diagnostics,debuginfo,coverage,object,all] Defines which scopes of paths should be remapped by `--remap-path-prefix` diff --git a/tests/rustdoc-html/auxiliary/remapped-paths.rs b/tests/rustdoc-html/auxiliary/remapped-paths.rs index f31d2d316f3aa..b361c012bc238 100644 --- a/tests/rustdoc-html/auxiliary/remapped-paths.rs +++ b/tests/rustdoc-html/auxiliary/remapped-paths.rs @@ -1,4 +1,4 @@ -//@ compile-flags:-Zunstable-options --remap-path-prefix={{src-base}}= +//@ compile-flags:--remap-path-prefix={{src-base}}= pub struct MyStruct { field: u32, diff --git a/tests/rustdoc-html/dyn-compatibility.rs b/tests/rustdoc-html/dyn-compatibility.rs index 9115f93bc3bac..2d13c8a88bfdb 100644 --- a/tests/rustdoc-html/dyn-compatibility.rs +++ b/tests/rustdoc-html/dyn-compatibility.rs @@ -15,8 +15,8 @@ pub trait DynIncompatible2 { } //@ has 'foo/trait.DynCompatible.html' -//@ !has - '//*[@class="dyn-compatibility-info"]' '' -//@ !has - '//*[@id="dyn-compatibility"]' '' +//@ has - '//*[@class="dyn-compatibility-info"]' 'This trait is dyn compatible.' +//@ has - '//*[@id="dyn-compatibility"]' 'Dyn Compatibility' pub trait DynCompatible { fn foo(&self); } diff --git a/tests/rustdoc-html/jump-to-def/prim-method.rs b/tests/rustdoc-html/jump-to-def/prim-method.rs new file mode 100644 index 0000000000000..43f6592de5390 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/prim-method.rs @@ -0,0 +1,16 @@ +// Checks that links to primitive types methods work. +// Regression test for . + +// ignore-tidy-linelength +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/prim-method.rs.html' + +fn scope() { + //@ has - '//a[@href="{{channel}}/core/primitive.usize.html#method.saturating_add"]' 'saturating_add' + let _ = 0usize.saturating_add(1); + //@ has - '//a[@href="{{channel}}/core/primitive.bool.html#method.then_some"]' 'then_some' + let _ = false.then_some(()); +} diff --git a/tests/rustdoc-html/jump-to-def/turbofish.rs b/tests/rustdoc-html/jump-to-def/turbofish.rs new file mode 100644 index 0000000000000..a2e8caa38bc9e --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/turbofish.rs @@ -0,0 +1,15 @@ +// This test ensures that turbofish (`::<...>`) does not prevent jump-to-definition +// links from being generated. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/turbofish.rs.html' +use std::marker::PhantomData as TheOne; + + +pub fn foo() { + //@ has - '//a[@href="{{channel}}/core/marker/struct.PhantomData.html"]' 'TheOne' + let _: TheOne::; +} diff --git a/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs b/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs index d003e19f200d6..f3c48f024b2de 100644 --- a/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs +++ b/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs @@ -1,7 +1,7 @@ // Regression test for remapped paths in rustdoc errors // . -//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ compile-flags:--remap-path-prefix={{src-base}}=remapped_path //@ rustc-env:RUST_BACKTRACE=0 #![deny(rustdoc::invalid_html_tags)] diff --git a/tests/rustdoc-ui/remap-path-prefix-doctest.rs b/tests/rustdoc-ui/remap-path-prefix-doctest.rs index 34fff98d5caa2..dcfc2ddf62e4f 100644 --- a/tests/rustdoc-ui/remap-path-prefix-doctest.rs +++ b/tests/rustdoc-ui/remap-path-prefix-doctest.rs @@ -9,7 +9,7 @@ //@ revisions: without-scope //@ compile-flags:--test --test-args --test-threads=1 -//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ compile-flags:--remap-path-prefix={{src-base}}=remapped_path //@[with-diag-scope] compile-flags: -Zunstable-options --remap-path-scope=diagnostics //@[with-macro-scope] compile-flags: -Zunstable-options --remap-path-scope=macro diff --git a/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs index 72c3330709a7c..003663f7d0aa5 100644 --- a/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs +++ b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs @@ -2,7 +2,7 @@ // adapted to use that, and that normalize line can go away //@ failure-status: 101 -//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ compile-flags:--test --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 //@ rustc-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout: "exit (status|code): 101" -> "exit status: 101" diff --git a/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs index c18a416d43f08..bed051e341a8f 100644 --- a/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs +++ b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs @@ -2,7 +2,7 @@ // adapted to use that, and that normalize line can go away //@ failure-status: 101 -//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ compile-flags:--test --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 //@ rustc-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" diff --git a/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs b/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs index 1be22694b8cdc..595451a7924bb 100644 --- a/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs +++ b/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs @@ -2,7 +2,7 @@ // when using --remap-path-prefix with macro rendering. // -//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ compile-flags:--remap-path-prefix={{src-base}}=remapped_path //@ rustc-env:RUST_BACKTRACE=0 //@ build-pass diff --git a/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs index 6fa04ef77f32b..5e6be3a2515d4 100644 --- a/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs +++ b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs @@ -4,7 +4,7 @@ // FIXME: if/when the output of the test harness can be tested on its own, this test should be // adapted to use that, and that normalize line can go away -//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ compile-flags:--test --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" // doctest passes at runtime diff --git a/tests/rustdoc-ui/remap-path-prefix.rs b/tests/rustdoc-ui/remap-path-prefix.rs index e3efa9a693490..8c6e2bde95717 100644 --- a/tests/rustdoc-ui/remap-path-prefix.rs +++ b/tests/rustdoc-ui/remap-path-prefix.rs @@ -7,11 +7,11 @@ //@ revisions: with-diag-scope with-macro-scope with-debuginfo-scope with-doc-scope //@ revisions: without-scopes without-remap -//@[with-diag-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[with-macro-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[with-debuginfo-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[with-doc-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[without-scopes] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped +//@[with-diag-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-macro-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-debuginfo-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-doc-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[without-scopes] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-diag-scope] compile-flags: -Zunstable-options --remap-path-scope=diagnostics //@[with-macro-scope] compile-flags: -Zunstable-options --remap-path-scope=macro diff --git a/tests/ui/README.md b/tests/ui/README.md index 39402f78bb5ec..f76cd507056fe 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -943,6 +943,9 @@ Tests on moves (destructive moves). Broad category of tests on mutability, such as the `mut` keyword, borrowing a value as both immutable and mutable (and the associated error), or adding mutable references to `const` declarations. +## `tests/ui/mut-restriction/` +Tests for `#![feature(mut_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077). + ## `tests/ui/namespace/` Contains a single test. It imports a massive amount of very similar types from a crate, then attempts various permutations of their namespace paths, checking for errors or the lackthereof. @@ -1244,6 +1247,10 @@ In this directory, multiple crates are compiled, but some of them have `inline` Tests on name shadowing. +## `tests/ui/share-trait` + +Tests for the unstable `Share` trait. + ## `tests/ui/shell-argfiles/`: `-Z shell-argfiles` command line flag The `-Zshell-argfiles` compiler flag allows argfiles to be parsed using POSIX "shell-style" quoting. When enabled, the compiler will use shlex to parse the arguments from argfiles specified with `@shell:`. diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index abe26ca91e5d8..55f2f552ad31c 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -6,7 +6,7 @@ #![feature(asm_unwind, linkage, rustc_attrs, cfg_target_object_format)] #![crate_type = "lib"] -use std::arch::{asm, global_asm, naked_asm}; +use std::arch::{asm, naked_asm}; #[unsafe(naked)] pub extern "C" fn inline_asm_macro() { @@ -14,12 +14,6 @@ pub extern "C" fn inline_asm_macro() { //~^ERROR the `asm!` macro is not allowed in naked functions } -#[unsafe(naked)] -pub extern "C" fn global_asm_macro() { - //~^ERROR naked functions must contain a single `naked_asm!` invocation - global_asm!(""); -} - #[repr(C)] pub struct P { x: u8, diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index e8963f7e1d52c..2b67c3aecd73c 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,71 +1,71 @@ error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:53:29 + --> $DIR/naked-functions.rs:47:29 | LL | naked_asm!("/* {0} */", in(reg) a) | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:74:10 + --> $DIR/naked-functions.rs:68:10 | LL | in(reg) a, | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `noreturn` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:94:28 + --> $DIR/naked-functions.rs:88:28 | LL | naked_asm!("", options(noreturn)); | ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly error: the `nomem` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:111:28 + --> $DIR/naked-functions.rs:105:28 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: the `preserves_flags` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:111:35 + --> $DIR/naked-functions.rs:105:35 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly error: the `readonly` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:28 + --> $DIR/naked-functions.rs:112:28 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly error: the `nostack` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:38 + --> $DIR/naked-functions.rs:112:38 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly error: the `pure` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:56 + --> $DIR/naked-functions.rs:112:56 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^ the `pure` option is not meaningful for global-scoped inline assembly error: the `may_unwind` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:126:28 + --> $DIR/naked-functions.rs:120:28 | LL | naked_asm!("", options(may_unwind)); | ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly error: this is a user specified error - --> $DIR/naked-functions.rs:157:5 + --> $DIR/naked-functions.rs:151:5 | LL | compile_error!("this is a user specified error") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this is a user specified error - --> $DIR/naked-functions.rs:163:5 + --> $DIR/naked-functions.rs:157:5 | LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:170:16 + --> $DIR/naked-functions.rs:164:16 | LL | naked_asm!(invalid_syntax) | ^^^^^^^^^^^^^^ @@ -76,38 +76,32 @@ error[E0787]: the `asm!` macro is not allowed in naked functions LL | unsafe { asm!("", options(raw)) }; | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead -error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:18:1 - | -LL | pub extern "C" fn global_asm_macro() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:31:5 + --> $DIR/naked-functions.rs:25:5 | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:33:5 + --> $DIR/naked-functions.rs:27:5 | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:35:6 + --> $DIR/naked-functions.rs:29:6 | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:37:5 + --> $DIR/naked-functions.rs:31:5 | LL | P { x, y }: P, | ^^^^^^^^^^ error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:46:5 + --> $DIR/naked-functions.rs:40:5 | LL | a + 1 | ^ @@ -115,7 +109,7 @@ LL | a + 1 = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:44:1 + --> $DIR/naked-functions.rs:38:1 | LL | pub extern "C" fn inc(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +118,7 @@ LL | a + 1 | ----- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:58:1 + --> $DIR/naked-functions.rs:52:1 | LL | pub extern "C" fn inc_closure(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +127,7 @@ LL | (|| a + 1)() | ------------ not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:64:1 + --> $DIR/naked-functions.rs:58:1 | LL | pub extern "C" fn unsupported_operands() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -150,13 +144,13 @@ LL | let mut e = 0usize; | ------------------- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:86:1 + --> $DIR/naked-functions.rs:80:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:91:1 + --> $DIR/naked-functions.rs:85:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +159,7 @@ LL | naked_asm!(""); | -------------- multiple `naked_asm!` invocations are not allowed in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:103:11 + --> $DIR/naked-functions.rs:97:11 | LL | *&y | ^ @@ -173,7 +167,7 @@ LL | *&y = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:101:5 + --> $DIR/naked-functions.rs:95:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,6 +175,6 @@ LL | LL | *&y | --- not allowed in naked functions -error: aborting due to 26 previous errors +error: aborting due to 25 previous errors For more information about this error, try `rustc --explain E0787`. diff --git a/tests/ui/asm/statement-global-asm-error.rs b/tests/ui/asm/statement-global-asm-error.rs deleted file mode 100644 index f32ba73357a19..0000000000000 --- a/tests/ui/asm/statement-global-asm-error.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ needs-asm-support - -use std::arch::global_asm; - -fn main() { - let x = 42; - global_asm!("{}", in(x)); - //~^ ERROR the `in` operand cannot be used with `global_asm!` - //~^^ NOTE the `in` operand is not meaningful for global-scoped inline assembly, remove it - - let y = global_asm!(""); - //~^ ERROR non-expression macro in expression position: global_asm -} diff --git a/tests/ui/asm/statement-global-asm-error.stderr b/tests/ui/asm/statement-global-asm-error.stderr deleted file mode 100644 index 35dde6ac1a781..0000000000000 --- a/tests/ui/asm/statement-global-asm-error.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: the `in` operand cannot be used with `global_asm!` - --> $DIR/statement-global-asm-error.rs:7:23 - | -LL | global_asm!("{}", in(x)); - | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it - -error: non-expression macro in expression position: global_asm - --> $DIR/statement-global-asm-error.rs:11:13 - | -LL | let y = global_asm!(""); - | ^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/asm/statement-global-asm.rs b/tests/ui/asm/statement-global-asm.rs deleted file mode 100644 index 597495546d955..0000000000000 --- a/tests/ui/asm/statement-global-asm.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ needs-asm-support -//@ run-pass - -use std::arch::global_asm; - -fn main() { - global_asm!(""); -} diff --git a/tests/ui/issues/issue-31299.rs b/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs similarity index 85% rename from tests/ui/issues/issue-31299.rs rename to tests/ui/associated-types/sized-recursive-type-via-associated-type.rs index b01b73bf373e8..2c2a3820d42d0 100644 --- a/tests/ui/issues/issue-31299.rs +++ b/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs @@ -1,6 +1,6 @@ +//! Regression test for //@ run-pass -// Regression test for #31299. This was generating an overflow error -// because of eager normalization: +// This was generating an overflow error because of eager normalization: // // proving `M: Sized` requires // - proving `PtrBack>: Sized` requires diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 784cbb38aaedd..4c9673bf80562 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -159,9 +159,8 @@ LL | #[unsafe(export_name)] | help: must be of the form | -LL - #[unsafe(export_name)] -LL + #[export_name = "name"] - | +LL | #[unsafe(export_name = "name")] + | ++++++++ error: `rustc_allow_const_fn_unstable` expects a list of feature names --> $DIR/malformed-attrs.rs:31:1 @@ -330,7 +329,7 @@ LL | #[unsafe(naked())] help: must be of the form | LL - #[unsafe(naked())] -LL + #[naked] +LL + #[unsafe(naked)] | error[E0565]: malformed `track_caller` attribute input @@ -651,7 +650,7 @@ LL | #[unsafe(ffi_pure = 1)] help: must be of the form | LL - #[unsafe(ffi_pure = 1)] -LL + #[ffi_pure] +LL + #[unsafe(ffi_pure)] | error[E0539]: malformed `link_ordinal` attribute input @@ -677,7 +676,7 @@ LL | #[unsafe(ffi_const = 1)] help: must be of the form | LL - #[unsafe(ffi_const = 1)] -LL + #[ffi_const] +LL + #[unsafe(ffi_const)] | error[E0539]: malformed `linkage` attribute input diff --git a/tests/ui/borrowck/dbg-issue-120327.rs b/tests/ui/borrowck/dbg-issue-120327.rs index 45605acc34334..2de43f634877a 100644 --- a/tests/ui/borrowck/dbg-issue-120327.rs +++ b/tests/ui/borrowck/dbg-issue-120327.rs @@ -1,44 +1,67 @@ -//! Diagnostic test for : suggest borrowing -//! variables passed to `dbg!` that are later used. -//@ dont-require-annotations: HELP - fn s() -> String { let a = String::new(); - dbg!(a); //~ HELP consider borrowing instead of transferring ownership + dbg!(a); return a; //~ ERROR use of moved value: } fn m() -> String { let a = String::new(); - dbg!(1, 2, a, 1, 2); //~ HELP consider borrowing instead of transferring ownership + dbg!(1, 2, a, 1, 2); return a; //~ ERROR use of moved value: } fn t(a: String) -> String { let b: String = "".to_string(); - dbg!(a, b); //~ HELP consider borrowing instead of transferring ownership + dbg!(a, b); return b; //~ ERROR use of moved value: } fn x(a: String) -> String { let b: String = "".to_string(); - dbg!(a, b); //~ HELP consider borrowing instead of transferring ownership + dbg!(a, b); return a; //~ ERROR use of moved value: } -fn two_of_them(a: String) -> String { - dbg!(a, a); //~ ERROR use of moved value - //~| HELP consider borrowing instead of transferring ownership - //~| HELP consider borrowing instead of transferring ownership - return a; //~ ERROR use of moved value +macro_rules! my_dbg { + () => { + eprintln!("[{}:{}:{}]", file!(), line!(), column!()) + }; + ($val:expr $(,)?) => { + match $val { + tmp => { + eprintln!("[{}:{}:{}] {} = {:#?}", + file!(), line!(), column!(), stringify!($val), &tmp); + tmp + } + } + }; + ($($val:expr),+ $(,)?) => { + ($(my_dbg!($val)),+,) + }; +} + +fn test_my_dbg() -> String { + let b: String = "".to_string(); + my_dbg!(b, 1); + return b; //~ ERROR use of moved value: +} + +fn test_not_macro() -> String { + let a = String::new(); + let _b = match a { + tmp => { + eprintln!("dbg: {}", tmp); + tmp + } + }; + return a; //~ ERROR use of moved value: } fn get_expr(_s: String) {} -// The suggestion is purely syntactic; applying it here will result in a type error. fn test() { let a: String = "".to_string(); - let _res = get_expr(dbg!(a)); //~ HELP consider borrowing instead of transferring ownership + let _res = get_expr(dbg!(a)); let _l = a.len(); //~ ERROR borrow of moved value } diff --git a/tests/ui/borrowck/dbg-issue-120327.stderr b/tests/ui/borrowck/dbg-issue-120327.stderr index e7a7151e541dd..efacc0c3f1341 100644 --- a/tests/ui/borrowck/dbg-issue-120327.stderr +++ b/tests/ui/borrowck/dbg-issue-120327.stderr @@ -1,133 +1,112 @@ error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:8:12 + --> $DIR/dbg-issue-120327.rs:4:12 | LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | dbg!(a); - | - value moved here + | ------- value moved here LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a.clone()); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(&a); | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:14:12 + --> $DIR/dbg-issue-120327.rs:10:12 | LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | dbg!(1, 2, a, 1, 2); - | - value moved here + | ------------------- value moved here LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(1, 2, a.clone(), 1, 2); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(1, 2, &a, 1, 2); | + error[E0382]: use of moved value: `b` - --> $DIR/dbg-issue-120327.rs:20:12 + --> $DIR/dbg-issue-120327.rs:16:12 | LL | let b: String = "".to_string(); | - move occurs because `b` has type `String`, which does not implement the `Copy` trait LL | dbg!(a, b); - | - value moved here + | ---------- value moved here LL | return b; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a, b.clone()); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(a, &b); | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:26:12 + --> $DIR/dbg-issue-120327.rs:22:12 | LL | fn x(a: String) -> String { | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | let b: String = "".to_string(); LL | dbg!(a, b); - | - value moved here + | ---------- value moved here LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a.clone(), b); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(&a, b); | + -error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:30:13 - | -LL | fn two_of_them(a: String) -> String { - | - move occurs because `a` has type `String`, which does not implement the `Copy` trait -LL | dbg!(a, a); - | - ^ value used here after move - | | - | value moved here +error[E0382]: use of moved value: `b` + --> $DIR/dbg-issue-120327.rs:46:12 | -help: consider cloning the value if the performance cost is acceptable +LL | tmp => { + | --- value moved here +... +LL | let b: String = "".to_string(); + | - move occurs because `b` has type `String`, which does not implement the `Copy` trait +LL | my_dbg!(b, 1); +LL | return b; + | ^ value used here after move | -LL | dbg!(a.clone(), a); - | ++++++++ help: consider borrowing instead of transferring ownership | -LL | dbg!(&a, a); - | + +LL | my_dbg!(&b, 1); + | + +help: borrow this binding in the pattern to avoid moving the value + | +LL | ref tmp => { + | +++ error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:33:12 + --> $DIR/dbg-issue-120327.rs:57:12 | -LL | fn two_of_them(a: String) -> String { - | - move occurs because `a` has type `String`, which does not implement the `Copy` trait -LL | dbg!(a, a); - | - value moved here +LL | let a = String::new(); + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | let _b = match a { +LL | tmp => { + | --- value moved here ... LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a, a.clone()); - | ++++++++ -help: consider borrowing instead of transferring ownership +help: borrow this binding in the pattern to avoid moving the value | -LL | dbg!(a, &a); - | + +LL | ref tmp => { + | +++ error[E0382]: borrow of moved value: `a` - --> $DIR/dbg-issue-120327.rs:42:14 + --> $DIR/dbg-issue-120327.rs:65:14 | LL | let a: String = "".to_string(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | let _res = get_expr(dbg!(a)); - | - value moved here + | ------- value moved here LL | let _l = a.len(); | ^ value borrowed here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | let _res = get_expr(dbg!(a.clone())); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | let _res = get_expr(dbg!(&a)); diff --git a/tests/ui/issues/issue-18188.rs b/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs similarity index 88% rename from tests/ui/issues/issue-18188.rs rename to tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs index b3b008229a53a..6f2c38843d754 100644 --- a/tests/ui/issues/issue-18188.rs +++ b/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18188 + //@ check-pass pub trait Promisable: Send + Sync {} diff --git a/tests/ui/issues/issue-3609.rs b/tests/ui/closures/boxed-closure-sent-through-thread.rs similarity index 90% rename from tests/ui/issues/issue-3609.rs rename to tests/ui/closures/boxed-closure-sent-through-thread.rs index a226e5b01362a..14320e0c4ab3c 100644 --- a/tests/ui/issues/issue-3609.rs +++ b/tests/ui/closures/boxed-closure-sent-through-thread.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/3609 + //@ check-pass #![allow(unused_must_use)] #![allow(dead_code)] diff --git a/tests/ui/issues/issue-3424.rs b/tests/ui/closures/boxed-closure-with-borrowed-param.rs similarity index 84% rename from tests/ui/issues/issue-3424.rs rename to tests/ui/closures/boxed-closure-with-borrowed-param.rs index 4d1a652142032..2d8dd4d800b85 100644 --- a/tests/ui/issues/issue-3424.rs +++ b/tests/ui/closures/boxed-closure-with-borrowed-param.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/3424 + //@ check-pass #![allow(dead_code)] #![allow(non_camel_case_types)] diff --git a/tests/ui/issues/issue-20575.rs b/tests/ui/closures/call-boxed-closure-no-args.rs similarity index 76% rename from tests/ui/issues/issue-20575.rs rename to tests/ui/closures/call-boxed-closure-no-args.rs index b213b79d37cab..bf6b73ee6fd57 100644 --- a/tests/ui/issues/issue-20575.rs +++ b/tests/ui/closures/call-boxed-closure-no-args.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/20575 + //@ run-pass // Test that overloaded calls work with zero arity closures diff --git a/tests/ui/issues/issue-17816.rs b/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs similarity index 81% rename from tests/ui/issues/issue-17816.rs rename to tests/ui/closures/call-closure-through-lifetime-generic-struct.rs index da28a14685f6c..aa2180e3c7f09 100644 --- a/tests/ui/issues/issue-17816.rs +++ b/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/17816 + //@ run-pass #![allow(unused_variables)] use std::marker::PhantomData; diff --git a/tests/ui/issues/issue-32389.rs b/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs similarity index 69% rename from tests/ui/issues/issue-32389.rs rename to tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs index 683c4874e8c20..2f1d29f77f27c 100644 --- a/tests/ui/issues/issue-32389.rs +++ b/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/32389 + //@ run-pass fn foo() -> T { loop {} } diff --git a/tests/ui/issues/issue-34349.rs b/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs similarity index 91% rename from tests/ui/issues/issue-34349.rs rename to tests/ui/closures/closure-kind-caching-during-upvar-inference.rs index d861802610aac..e24e23d49d555 100644 --- a/tests/ui/issues/issue-34349.rs +++ b/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs @@ -1,3 +1,5 @@ +//! Issue: https://github.com/rust-lang/rust/issues/34349 + // This is a regression test for a problem encountered around upvar // inference and trait caching: in particular, we were entering a // temporary closure kind during inference, and then caching results diff --git a/tests/ui/issues/issue-34349.stderr b/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr similarity index 85% rename from tests/ui/issues/issue-34349.stderr rename to tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr index 6a6188f10c8f8..291ad5960bc21 100644 --- a/tests/ui/issues/issue-34349.stderr +++ b/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr @@ -1,5 +1,5 @@ error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut` - --> $DIR/issue-34349.rs:16:17 + --> $DIR/closure-kind-caching-during-upvar-inference.rs:18:17 | LL | let diary = || { | ^^ this closure implements `FnMut`, not `Fn` @@ -12,7 +12,7 @@ LL | apply(diary); | required by a bound introduced by this call | note: required by a bound in `apply` - --> $DIR/issue-34349.rs:11:32 + --> $DIR/closure-kind-caching-during-upvar-inference.rs:13:32 | LL | fn apply(f: F) where F: Fn() { | ^^^^ required by this bound in `apply` diff --git a/tests/ui/issues/issue-40000.rs b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs similarity index 75% rename from tests/ui/issues/issue-40000.rs rename to tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs index a6e05e7ba02a3..74d5470f89665 100644 --- a/tests/ui/issues/issue-40000.rs +++ b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/40000 + fn main() { let bar: fn(&mut u32) = |_| {}; diff --git a/tests/ui/issues/issue-40000.stderr b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr similarity index 85% rename from tests/ui/issues/issue-40000.stderr rename to tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr index 0737a9610e294..7466751e43b51 100644 --- a/tests/ui/issues/issue-40000.stderr +++ b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-40000.rs:6:9 + --> $DIR/closure-to-fn-pointer-lifetime-error.rs:8:9 | LL | foo(bar); | ^^^ one type is more general than the other @@ -8,7 +8,7 @@ LL | foo(bar); found trait object `dyn Fn(&i32)` error[E0308]: mismatched types - --> $DIR/issue-40000.rs:6:9 + --> $DIR/closure-to-fn-pointer-lifetime-error.rs:8:9 | LL | foo(bar); | ^^^ one type is more general than the other diff --git a/tests/ui/issues/issue-28181.rs b/tests/ui/closures/closure-with-fixed-size-array-param.rs similarity index 62% rename from tests/ui/issues/issue-28181.rs rename to tests/ui/closures/closure-with-fixed-size-array-param.rs index e1cb5ba1c8828..f12df2e5f628a 100644 --- a/tests/ui/issues/issue-28181.rs +++ b/tests/ui/closures/closure-with-fixed-size-array-param.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/28181 + //@ run-pass fn bar(f: F) -> usize where F: Fn([usize; 1]) -> usize { f([2]) } diff --git a/tests/ui/issues/issue-26484.rs b/tests/ui/closures/debug-info-for-closure-capture.rs similarity index 68% rename from tests/ui/issues/issue-26484.rs rename to tests/ui/closures/debug-info-for-closure-capture.rs index c7053505567ab..2a8db27bda805 100644 --- a/tests/ui/issues/issue-26484.rs +++ b/tests/ui/closures/debug-info-for-closure-capture.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/26484 + //@ run-pass //@ compile-flags:-g diff --git a/tests/ui/issues/issue-20174.rs b/tests/ui/closures/destructure-newtype-closure.rs similarity index 68% rename from tests/ui/issues/issue-20174.rs rename to tests/ui/closures/destructure-newtype-closure.rs index 7b49fe8c7b472..f8543e64cb353 100644 --- a/tests/ui/issues/issue-20174.rs +++ b/tests/ui/closures/destructure-newtype-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/20174 + //@ run-pass struct GradFn usize>(F); diff --git a/tests/ui/issues/issue-36260.rs b/tests/ui/closures/drop-glue-for-closure-with-captures.rs similarity index 81% rename from tests/ui/issues/issue-36260.rs rename to tests/ui/closures/drop-glue-for-closure-with-captures.rs index 265b0d2f80217..2ccf4c31a995e 100644 --- a/tests/ui/issues/issue-36260.rs +++ b/tests/ui/closures/drop-glue-for-closure-with-captures.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/36260 + //@ run-pass // Make sure this compiles without getting a linker error because of missing // drop-glue because the collector missed adding drop-glue for the closure: diff --git a/tests/ui/issues/issue-22346.rs b/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs similarity index 77% rename from tests/ui/issues/issue-22346.rs rename to tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs index 710dc0acda7e9..74e0ea345dd62 100644 --- a/tests/ui/issues/issue-22346.rs +++ b/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/22346 + //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/issues/issue-17897.rs b/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs similarity index 68% rename from tests/ui/issues/issue-17897.rs rename to tests/ui/closures/fn-sugar-in-boxed-trait-object.rs index dbb560a199bf7..8f475057f376d 100644 --- a/tests/ui/issues/issue-17897.rs +++ b/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/17897 + //@ run-pass fn action(mut cb: Box usize>) -> usize { cb(1) diff --git a/tests/ui/issues/issue-23046.rs b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs similarity index 88% rename from tests/ui/issues/issue-23046.rs rename to tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs index a68369616d8b6..e66603a1f7dc6 100644 --- a/tests/ui/issues/issue-23046.rs +++ b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/23046 + pub enum Expr<'var, VAR> { Let(Box>, Box Fn(Expr<'v, VAR>) -> Expr<'v, VAR> + 'var>) diff --git a/tests/ui/issues/issue-23046.stderr b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr similarity index 87% rename from tests/ui/issues/issue-23046.stderr rename to tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr index f70ac0c9f388a..56ebb0a339a50 100644 --- a/tests/ui/issues/issue-23046.stderr +++ b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed for `Expr<'_, _>` - --> $DIR/issue-23046.rs:17:15 + --> $DIR/hrtb-closure-in-recursive-enum-type-error.rs:19:15 | LL | let ex = |x| { | ^ diff --git a/tests/ui/issues/issue-29522.rs b/tests/ui/closures/module-path-not-confused-with-upvar.rs similarity index 80% rename from tests/ui/issues/issue-29522.rs rename to tests/ui/closures/module-path-not-confused-with-upvar.rs index 2a39ef28bdbbc..9eb56bee2f33c 100644 --- a/tests/ui/issues/issue-29522.rs +++ b/tests/ui/closures/module-path-not-confused-with-upvar.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/29522 + //@ run-pass #![allow(unused_variables)] // check that we don't accidentally capture upvars just because their name diff --git a/tests/ui/issues/issue-16668.rs b/tests/ui/closures/move-closure-can-call-captured-fnmut.rs similarity index 87% rename from tests/ui/issues/issue-16668.rs rename to tests/ui/closures/move-closure-can-call-captured-fnmut.rs index 2b2370b0e2b4c..8267dd513417a 100644 --- a/tests/ui/issues/issue-16668.rs +++ b/tests/ui/closures/move-closure-can-call-captured-fnmut.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16668 + //@ check-pass #![allow(dead_code)] struct Parser<'a, I, O> { diff --git a/tests/ui/issues/issue-16560.rs b/tests/ui/closures/move-closure-is-send.rs similarity index 82% rename from tests/ui/issues/issue-16560.rs rename to tests/ui/closures/move-closure-is-send.rs index d9a7aa9101d3f..ace163a37d839 100644 --- a/tests/ui/issues/issue-16560.rs +++ b/tests/ui/closures/move-closure-is-send.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16560 + //@ run-pass #![allow(unused_variables)] //@ needs-threads diff --git a/tests/ui/issues/issue-16671.rs b/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs similarity index 68% rename from tests/ui/issues/issue-16671.rs rename to tests/ui/closures/no-unused-mut-warning-for-move-closure.rs index f7f4f4348afab..a8c8aae3bb299 100644 --- a/tests/ui/issues/issue-16671.rs +++ b/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16671 + //@ run-pass #![deny(warnings)] diff --git a/tests/ui/issues/issue-19127.rs b/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs similarity index 68% rename from tests/ui/issues/issue-19127.rs rename to tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs index 2172c631b841d..2da3ac24eb5b0 100644 --- a/tests/ui/issues/issue-19127.rs +++ b/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/19127 + //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/issues/issue-16994.rs b/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs similarity index 72% rename from tests/ui/issues/issue-16994.rs rename to tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs index fa3988e099945..72648a92decf3 100644 --- a/tests/ui/issues/issue-16994.rs +++ b/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16994 + //@ check-pass fn cb<'a,T>(_x: Box, bool))) -> T>) -> T { diff --git a/tests/ui/issues/issue-46069.rs b/tests/ui/closures/region-obligations-closure-and-projection.rs similarity index 86% rename from tests/ui/issues/issue-46069.rs rename to tests/ui/closures/region-obligations-closure-and-projection.rs index adfb567d7dd32..9010b7c8c6bed 100644 --- a/tests/ui/issues/issue-46069.rs +++ b/tests/ui/closures/region-obligations-closure-and-projection.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/46069 + //@ run-pass use std::iter::{Fuse, Cloned}; use std::slice::Iter; diff --git a/tests/ui/issues/issue-24036.rs b/tests/ui/closures/unique-closure-type-mismatch.rs similarity index 80% rename from tests/ui/issues/issue-24036.rs rename to tests/ui/closures/unique-closure-type-mismatch.rs index 7df036c8e3a45..ee05c9ed77e40 100644 --- a/tests/ui/issues/issue-24036.rs +++ b/tests/ui/closures/unique-closure-type-mismatch.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/24036 + fn closure_to_loc() { let mut x = |c| c + 1; x = |c| c + 1; diff --git a/tests/ui/issues/issue-24036.stderr b/tests/ui/closures/unique-closure-type-mismatch.stderr similarity index 75% rename from tests/ui/issues/issue-24036.stderr rename to tests/ui/closures/unique-closure-type-mismatch.stderr index 184383b736942..3a31b6db4f62b 100644 --- a/tests/ui/issues/issue-24036.stderr +++ b/tests/ui/closures/unique-closure-type-mismatch.stderr @@ -1,18 +1,18 @@ error[E0308]: mismatched types - --> $DIR/issue-24036.rs:3:9 + --> $DIR/unique-closure-type-mismatch.rs:5:9 | LL | let mut x = |c| c + 1; | --- the expected closure LL | x = |c| c + 1; | ^^^^^^^^^ expected closure, found a different closure | - = note: expected closure `{closure@$DIR/issue-24036.rs:2:17: 2:20}` - found closure `{closure@$DIR/issue-24036.rs:3:9: 3:12}` + = note: expected closure `{closure@$DIR/unique-closure-type-mismatch.rs:4:17: 4:20}` + found closure `{closure@$DIR/unique-closure-type-mismatch.rs:5:9: 5:12}` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object error[E0284]: type annotations needed - --> $DIR/issue-24036.rs:9:15 + --> $DIR/unique-closure-type-mismatch.rs:11:15 | LL | 1 => |c| c + 1, | ^ - type must be known at this point diff --git a/tests/ui/issues/issue-27997.rs b/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs similarity index 91% rename from tests/ui/issues/issue-27997.rs rename to tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs index 85317cec061ad..1a81632970c97 100644 --- a/tests/ui/issues/issue-27997.rs +++ b/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass use std::sync::atomic::{Ordering, AtomicUsize}; diff --git a/tests/ui/issues/issue-35815.rs b/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs similarity index 80% rename from tests/ui/issues/issue-35815.rs rename to tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs index 1a09d8041e459..19bf627e6ddf2 100644 --- a/tests/ui/issues/issue-35815.rs +++ b/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![allow(dead_code)] use std::mem; diff --git a/tests/ui/issues/issue-16922-rpass.rs b/tests/ui/coercion/fn-item-to-dyn-any.rs similarity index 56% rename from tests/ui/issues/issue-16922-rpass.rs rename to tests/ui/coercion/fn-item-to-dyn-any.rs index f7ffcfb1d94e3..0e6e5aa5f89ce 100644 --- a/tests/ui/issues/issue-16922-rpass.rs +++ b/tests/ui/coercion/fn-item-to-dyn-any.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-pass use std::any::Any; diff --git a/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs new file mode 100644 index 0000000000000..b31603b21b5fb --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs @@ -0,0 +1,18 @@ +// Regression test for https://github.com/rust-lang/rust/issues/142913. + +// A const parameter introduced by `generic_const_parameter_types`, +// whose type mentions an earlier lifetime +// parameter and which carries a default value, used to ICE in `rustc_type_ir` +// with "region parameter out of range when instantiating args". It is now +// rejected with ordinary errors instead of crashing. + +#![feature(generic_const_parameter_types)] + +struct Variant; + +fn foo<'a, const N: &'a Variant = {}>() {} +//~^ ERROR: defaults for generic parameters are not allowed here +//~| ERROR: anonymous constants with lifetimes in their type are not yet supported +//~| ERROR: `&'a Variant` is forbidden as the type of a const generic parameter + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr new file mode 100644 index 0000000000000..bdcf172cf81f3 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr @@ -0,0 +1,30 @@ +error: defaults for generic parameters are not allowed here + --> $DIR/lifetime_dependent_const_param_default.rs:13:12 + | +LL | fn foo<'a, const N: &'a Variant = {}>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: anonymous constants with lifetimes in their type are not yet supported + --> $DIR/lifetime_dependent_const_param_default.rs:13:35 + | +LL | fn foo<'a, const N: &'a Variant = {}>() {} + | ^^ + +error: `&'a Variant` is forbidden as the type of a const generic parameter + --> $DIR/lifetime_dependent_const_param_default.rs:13:21 + | +LL | fn foo<'a, const N: &'a Variant = {}>() {} + | ^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool`, and `char` +help: add `#![feature(min_adt_const_params)]` to the crate attributes to enable more complex and user defined types + | +LL + #![feature(min_adt_const_params)] + | +help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait + | +LL + #![feature(unsized_const_params)] + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/issues/issue-47486.rs b/tests/ui/consts/size_of-requires-type-annotation-in-const.rs similarity index 67% rename from tests/ui/issues/issue-47486.rs rename to tests/ui/consts/size_of-requires-type-annotation-in-const.rs index d686f02a9fe39..3e4a9fd076b47 100644 --- a/tests/ui/issues/issue-47486.rs +++ b/tests/ui/consts/size_of-requires-type-annotation-in-const.rs @@ -1,3 +1,4 @@ +//! Regression test for fn main() { () < std::mem::size_of::<_>(); //~ ERROR: mismatched types [0u8; std::mem::size_of::<_>()]; //~ ERROR: type annotations needed diff --git a/tests/ui/issues/issue-47486.stderr b/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr similarity index 82% rename from tests/ui/issues/issue-47486.stderr rename to tests/ui/consts/size_of-requires-type-annotation-in-const.stderr index c7e9af70e64a7..3ca15af5e1782 100644 --- a/tests/ui/issues/issue-47486.stderr +++ b/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-47486.rs:2:10 + --> $DIR/size_of-requires-type-annotation-in-const.rs:3:10 | LL | () < std::mem::size_of::<_>(); | -- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `usize` @@ -7,7 +7,7 @@ LL | () < std::mem::size_of::<_>(); | expected because this is `()` error[E0282]: type annotations needed - --> $DIR/issue-47486.rs:3:11 + --> $DIR/size_of-requires-type-annotation-in-const.rs:4:11 | LL | [0u8; std::mem::size_of::<_>()]; | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `size_of` diff --git a/tests/ui/issues/auxiliary/issue-17662.rs b/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs similarity index 80% rename from tests/ui/issues/auxiliary/issue-17662.rs rename to tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs index 5ecec31deb018..b2265886cb243 100644 --- a/tests/ui/issues/auxiliary/issue-17662.rs +++ b/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs @@ -1,3 +1,4 @@ +//! Auxiliary crate for . #![crate_type = "lib"] pub trait Foo<'a, T> { diff --git a/tests/ui/issues/issue-17662.rs b/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs similarity index 57% rename from tests/ui/issues/issue-17662.rs rename to tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs index e75613e04d333..aacc91f5f6f99 100644 --- a/tests/ui/issues/issue-17662.rs +++ b/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs @@ -1,8 +1,8 @@ +//! Regression test for . //@ run-pass -//@ aux-build:issue-17662.rs +//@ aux-build:dyn-trait-lifetime-infer-metadata.rs - -extern crate issue_17662 as i; +extern crate dyn_trait_lifetime_infer_metadata as i; use std::marker; diff --git a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr index ab0c6d66e9c51..9537e45f3b8f0 100644 --- a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr +++ b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr @@ -65,13 +65,20 @@ LL | impl Trait for Z { | ^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:53 + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:44 | LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>` + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found `&InvariantRef<'_, ()>` + | | + | arguments to this function are incorrect | - = note: expected type `u8` - found struct `InvariantRef<'_, ()>` + = note: expected reference `&u8` + found reference `&InvariantRef<'_, ()>` +note: method defined here + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:13:8 + | +LL | fn foo(&self) -> u8 { 0 } + | ^^^ ----- error[E0277]: the trait bound `u8: Trait` is not satisfied --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:12 @@ -87,14 +94,20 @@ LL | impl Trait for Z { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:53 + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:44 | LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>` + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found `&InvariantRef<'_, ()>` + | | + | arguments to this function are incorrect | - = note: expected type `u8` - found struct `InvariantRef<'_, ()>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: expected reference `&u8` + found reference `&InvariantRef<'_, ()>` +note: method defined here + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:14:8 + | +LL | fn bar(&self) -> u8 { 1 } + | ^^^ ----- error[E0277]: the trait bound `u8: Trait` is not satisfied --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:12 @@ -110,14 +123,20 @@ LL | impl Trait for Z { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:53 + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:44 | LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>` + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found `&InvariantRef<'_, ()>` + | | + | arguments to this function are incorrect | - = note: expected type `u8` - found struct `InvariantRef<'_, ()>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: expected reference `&u8` + found reference `&InvariantRef<'_, ()>` +note: method defined here + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:15:8 + | +LL | fn meh(&self) -> u8 { 2 } + | ^^^ ----- error: aborting due to 10 previous errors diff --git a/tests/ui/delegation/generics/generics-aux-pass.rs b/tests/ui/delegation/generics/generics-aux-pass.rs index 06f5c6d4d212e..f5d49c03a6137 100644 --- a/tests/ui/delegation/generics/generics-aux-pass.rs +++ b/tests/ui/delegation/generics/generics-aux-pass.rs @@ -16,20 +16,17 @@ impl generics::Trait<'static, i32, 1> for X {} impl X { reuse generics::foo as bar; - reuse generics::Trait::foo as trait_foo; reuse generics::foo::<'static, 'static, i32, i32, 1> as bar1; reuse generics::Trait::<'static, i32, 1>::foo::<'static, i32, false> as trait_foo1; } trait LocalTrait { - fn get() -> u8 { 123 } fn get_self(&self) -> u8 { 123 } reuse generics::foo as bar; reuse generics::foo::<'static, 'static, i32, i32, 1> as bar1; - reuse generics::Trait::foo as trait_foo { Self::get() } reuse generics::Trait::<'static, i32, 1>::foo::<'static, i32, false> as trait_foo1 { Self::get_self(&self) } @@ -50,13 +47,11 @@ fn main() { X::bar::(); X::bar::<'static, 'static, i32, i32, 1>(); X::bar1(); - x.trait_foo::<'static, 'static, i32, 1, String, true>(); x.trait_foo1(); ::bar::(); ::bar::<'static, 'static, i32, i32, 1>(); ::bar1(); - 1usize.trait_foo::<'static, 'static, i32, 1, String, true>(); 1usize.trait_foo1(); } diff --git a/tests/ui/delegation/generics/impl-to-trait-method.rs b/tests/ui/delegation/generics/impl-to-trait-method.rs index 102e905068e2a..88d6e4fdaabef 100644 --- a/tests/ui/delegation/generics/impl-to-trait-method.rs +++ b/tests/ui/delegation/generics/impl-to-trait-method.rs @@ -36,7 +36,6 @@ mod unconstrained_parameter { struct S(F); impl S { reuse Trait::foo { &self.0 } - //~^ ERROR type annotations needed } } diff --git a/tests/ui/delegation/generics/impl-to-trait-method.stderr b/tests/ui/delegation/generics/impl-to-trait-method.stderr index 114ebf48cca06..34259d49a1bd9 100644 --- a/tests/ui/delegation/generics/impl-to-trait-method.stderr +++ b/tests/ui/delegation/generics/impl-to-trait-method.stderr @@ -46,13 +46,6 @@ LL | fn foo(&self) LL | Self: Trait0, | ^^^^^^ required by this bound in `Trait1::foo` -error[E0282]: type annotations needed - --> $DIR/impl-to-trait-method.rs:38:22 - | -LL | reuse Trait::foo { &self.0 } - | ^^^ cannot infer type for type parameter `T` declared on the trait `Trait` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, E0282. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.rs b/tests/ui/delegation/ice-line-bounds-issue-148732.rs index 699e7d86f2581..0123f0c8705b0 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.rs +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.rs @@ -2,7 +2,7 @@ reuse a as b { //~^ ERROR cannot find function `a` in this scope //~| ERROR functions delegation is not yet fully implemented dbg!(b); - //~^ ERROR missing lifetime specifier + //~^ ERROR: `fn() {b}` doesn't implement `Debug` } fn main() {} diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index c4f261181b109..eb93655beb60d 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -1,9 +1,3 @@ -error[E0106]: missing lifetime specifier - --> $DIR/ice-line-bounds-issue-148732.rs:4:5 - | -LL | dbg!(b); - | ^^^^^^^ expected named lifetime parameter - error[E0425]: cannot find function `a` in this scope --> $DIR/ice-line-bounds-issue-148732.rs:1:7 | @@ -25,7 +19,18 @@ LL | | } = help: add `#![feature(fn_delegation)]` 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[E0277]: `fn() {b}` doesn't implement `Debug` + --> $DIR/ice-line-bounds-issue-148732.rs:4:5 + | +LL | reuse a as b { + | - consider calling this function +... +LL | dbg!(b); + | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` + | + = help: use parentheses to call this function: `b()` + error: aborting due to 3 previous errors -Some errors have detailed explanations: E0106, E0425, E0658. -For more information about an error, try `rustc --explain E0106`. +Some errors have detailed explanations: E0277, E0425, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/self-coercion-errors.rs b/tests/ui/delegation/self-coercion-errors.rs new file mode 100644 index 0000000000000..6c8356bd7495b --- /dev/null +++ b/tests/ui/delegation/self-coercion-errors.rs @@ -0,0 +1,234 @@ +// Tests below represent situations when type of the first argument can not be adjusted +// to the type of first parameter (i.e., Rc -> &mut T). + +#![feature(fn_delegation)] + +use std::sync::Arc; +use std::pin::Pin; +use std::rc::Rc; + +trait Trait: Sized { + fn by_value(self) -> i32 { 1 } + fn by_mut_ref(&mut self) -> i32 { 2 } + fn by_ref(&self) -> i32 { 3 } + fn r#box(self: Box) -> i32 { 4 } + fn arc(self: Arc) -> i32 { 5 } + fn rc(self: Rc) -> i32 { 6 } + fn pin_box(self: Pin>) -> i32 { 7 } + fn pin_rc(self: Pin>) -> i32 { 8 } + fn pin_arc(self: Pin>) -> i32 { 9 } + fn box_box(self: Box>) -> i32 { 10 } +} + +struct F; +impl Trait for F {} + +struct S(F); + +fn foo() -> F { + F +} + +impl S { + reuse Trait::{by_value, by_mut_ref, by_ref} { + println!("123"); + let x = &self.0; + foo() + } +} + +struct S1(F); + +impl S1 { + reuse Trait::{by_value, by_mut_ref, by_ref} { + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: the trait bound `fn() -> F {foo}: Trait` is not satisfied + println!("123"); + let x = &self.0; + foo + } +} + +struct S2(F); + +impl S2 { + reuse Trait::{by_value, by_mut_ref, by_ref} { + println!("123"); + let x = &self.0; + let x = foo(); + + x + } +} + +struct S3(F); + +impl S3 { + reuse Trait::{by_value, by_mut_ref, by_ref} { + println!("123"); + let x = &self.0; + let x = foo(); + + &mut x + //~^ ERROR: cannot borrow `x` as mutable, as it is not declared as mutable + //~| ERROR: cannot borrow `x` as mutable, as it is not declared as mutable + //~| ERROR: cannot borrow `x` as mutable, as it is not declared as mutable + //~| ERROR: cannot move out of a mutable reference + } +} + +struct X(F); + +impl X { + reuse Trait::* { &mut self.0 } + //~^ ERROR: cannot borrow `self.0` as mutable, as it is behind a `&` reference + //~| ERROR: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + //~| ERROR: cannot move out of a mutable reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X1(F); + +impl X1 { + reuse Trait::* { &self.0 } + //~^ ERROR: cannot borrow data in a `&` reference as mutable + //~| ERROR: cannot move out of a shared reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X2(F); + +impl X2 { + reuse Trait::* { &&&&self.0 } + //~^ ERROR: cannot move out of a shared reference + //~| ERROR: cannot borrow data in a `&` reference as mutable + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X3(Box); + +impl X3 { + reuse Trait::* { self.0.as_ref() } + //~^ ERROR: cannot borrow data in a `&` reference as mutable + //~| ERROR: cannot move out of a shared reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X4(F); + +impl X4 { + reuse Trait::* { &mut &mut &mut self.0 } + //~^ ERROR: cannot move out of a mutable reference + //~| ERROR: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + //~| ERROR: cannot borrow `self.0` as mutable, as it is behind a `&` reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X5(F); + +impl X5 { + reuse Trait::* { &&mut self.0 } + //~^ ERROR: cannot borrow `self.0` as mutable, as it is behind a `&` reference + //~| ERROR: cannot borrow data in a `&` reference as mutable [E0596] + //~| ERROR: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + //~| ERROR: cannot move out of a shared reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X6(Box); + +impl X6 { + reuse Trait::* { self.0 } + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X7(Box>>); + +impl X7 { + reuse Trait::* { self.0 } + //~^ ERROR: cannot borrow data in an `Arc` as mutable + //~| ERROR: cannot move out of an `Arc` + //~| ERROR: cannot move out of an `Arc` + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X8(Pin>); + +impl X8 { + reuse Trait::* { self.0 } + //~^ ERROR: cannot move out of dereference of `Pin>` + //~| ERROR: cannot borrow data in dereference of `Pin>` as mutable + //~| ERROR: cannot move out of dereference of `Pin>` + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct OtherStruct; +struct X9(OtherStruct); + +impl X9 { + reuse Trait::* { self.0 } + //~^ ERROR: the trait bound `OtherStruct: Trait` is not satisfied + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +fn main() {} diff --git a/tests/ui/delegation/self-coercion-errors.stderr b/tests/ui/delegation/self-coercion-errors.stderr new file mode 100644 index 0000000000000..89308b15d2d84 --- /dev/null +++ b/tests/ui/delegation/self-coercion-errors.stderr @@ -0,0 +1,1916 @@ +error[E0277]: the trait bound `fn() -> F {foo}: Trait` is not satisfied + --> $DIR/self-coercion-errors.rs:43:49 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | ___________________--------______________________^ + | | | + | | required by a bound introduced by this call +... | +LL | | foo + | | --- this tail expression is of type `fn() -> F {foo}` +LL | | } + | |_____^ the trait `Trait` is not implemented for fn item `fn() -> F {foo}` + | +help: use parentheses to call this function + | +LL | }() + | ++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:43:49 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | _____________________________----------__________^ + | | | + | | arguments to this function are incorrect +... | +LL | | foo +LL | | } + | |_____^ expected `&mut _`, found fn item + | + = note: expected mutable reference `&mut _` + found fn item `fn() -> F {foo}` +note: method defined here + --> $DIR/self-coercion-errors.rs:12:8 + | +LL | fn by_mut_ref(&mut self) -> i32 { 2 } + | ^^^^^^^^^^ --------- +help: consider mutably borrowing here + | +LL | &mut foo + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:43:49 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | _________________________________________------__^ + | | | + | | arguments to this function are incorrect +... | +LL | | foo +LL | | } + | |_____^ expected `&_`, found fn item + | + = note: expected reference `&_` + found fn item `fn() -> F {foo}` +note: method defined here + --> $DIR/self-coercion-errors.rs:13:8 + | +LL | fn by_ref(&self) -> i32 { 3 } + | ^^^^^^ ----- +help: consider borrowing here + | +LL | &foo + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Box<_>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found mutable reference `&mut F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Arc<_>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&mut F` into `Arc<_>` + | +LL | reuse Trait::* { (&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Rc<_>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&mut F` into `Rc<_>` + | +LL | reuse Trait::* { (&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Pin>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Pin>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Pin>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Box>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Box<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Arc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Arc<_>` + | +LL | reuse Trait::* { (&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Rc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Rc<_>` + | +LL | reuse Trait::* { (&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Box>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Box<_>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&&&&F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&&&&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Arc<_>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&&&&F` into `Arc<_>` + | +LL | reuse Trait::* { (&&&&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Rc<_>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&&&&F` into `Rc<_>` + | +LL | reuse Trait::* { (&&&&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Pin>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&&&&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Pin>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Pin>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Box>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Box<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: try removing the method call + | +LL - reuse Trait::* { self.0.as_ref() } +LL + reuse Trait::* { self.0 } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Arc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Arc<_>` + | +LL | reuse Trait::* { self.0.as_ref().into() } + | +++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Rc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Rc<_>` + | +LL | reuse Trait::* { self.0.as_ref().into() } + | +++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(self.0.as_ref()) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Box>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Box<_>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found mutable reference `&mut &mut &mut F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&mut &mut &mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Arc<_>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&mut &mut &mut F` into `Arc<_>` + | +LL | reuse Trait::* { (&mut &mut &mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Rc<_>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&mut &mut &mut F` into `Rc<_>` + | +LL | reuse Trait::* { (&mut &mut &mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Pin>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&mut &mut &mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Pin>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Pin>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Box>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Box<_>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&&mut F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Arc<_>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&&mut F` into `Arc<_>` + | +LL | reuse Trait::* { (&&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Rc<_>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&&mut F` into `Rc<_>` + | +LL | reuse Trait::* { (&&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Pin>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Pin>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Pin>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Box>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `Box` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X6` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:175:8 + | +LL | struct X6(Box); + | -- ------ this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider unboxing the value + | +LL | reuse Trait::* { *self.0 } + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `Box>>` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X7` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:187:8 + | +LL | struct X7(Box>>); + | -- ---------------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box<_>`, found `Pin>` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box` with type `std::ptr::Unique` but it is private; `0` from `X8` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:202:8 + | +LL | struct X8(Pin>); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box<_>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `Pin>` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X8` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:202:8 + | +LL | struct X8(Pin>); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0277]: the trait bound `OtherStruct: Trait` is not satisfied + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `Trait` is not implemented for `OtherStruct` + --> $DIR/self-coercion-errors.rs:217:1 + | +LL | struct OtherStruct; + | ^^^^^^^^^^^^^^^^^^ +help: the trait `Trait` is implemented for `F` + --> $DIR/self-coercion-errors.rs:24:1 + | +LL | impl Trait for F {} + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `&mut _`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected mutable reference `&mut _` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:12:8 + | +LL | fn by_mut_ref(&mut self) -> i32 { 2 } + | ^^^^^^^^^^ --------- +help: consider mutably borrowing here + | +LL | reuse Trait::* { &mut self.0 } + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `&_`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:13:8 + | +LL | fn by_ref(&self) -> i32 { 3 } + | ^^^^^^ ----- +help: consider borrowing here + | +LL | reuse Trait::* { &self.0 } + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box<_>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box` with type `std::ptr::Unique` but it is private; `0` from `X9` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:218:8 + | +LL | struct X9(OtherStruct); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box<_>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X9` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:218:8 + | +LL | struct X9(OtherStruct); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0507]: cannot move out of a mutable reference + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | -------- value moved due to this method call +... +LL | &mut x + | ^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | &mut x + | ------ you could clone this value + +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | &mut x + | ^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | let mut x = foo(); + | +++ + +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | &mut x + | ^^^^^^ cannot borrow as mutable + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider changing this to be mutable + | +LL | let mut x = foo(); + | +++ + +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | &mut x + | ^^^^^^ cannot borrow as mutable + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider changing this to be mutable + | +LL | let mut x = foo(); + | +++ + +error[E0507]: cannot move out of a mutable reference + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &mut self.0 } + | ----------- you could clone this value + +error[E0596]: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | ^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | reuse Trait::mut * { &mut self.0 } + | +++ + +error[E0596]: cannot borrow `self.0` as mutable, as it is behind a `&` reference + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | ^^^^^^^^^^^ `self` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - reuse Trait::* { &mut self.0 } +LL + reuse Trait::&mut self { &mut self.0 } + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &self.0 } + | ------- you could clone this value + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | ^^^^^^^ cannot borrow as mutable + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &&&&self.0 } + | ---------- you could clone this value + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | ^^^^^^^^^^ cannot borrow as mutable + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0.as_ref() } + | --------------- you could clone this value + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | ^^^^^^^^^^^^^^^ cannot borrow as mutable + +error[E0507]: cannot move out of a mutable reference + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &mut &mut &mut self.0 } + | --------------------- you could clone this value + +error[E0596]: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + --> $DIR/self-coercion-errors.rs:145:32 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | ^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | reuse Trait::mut * { &mut &mut &mut self.0 } + | +++ + +error[E0596]: cannot borrow `self.0` as mutable, as it is behind a `&` reference + --> $DIR/self-coercion-errors.rs:145:32 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | ^^^^^^^^^^^ `self` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - reuse Trait::* { &mut &mut &mut self.0 } +LL + reuse Trait::&mut self { &mut &mut &mut self.0 } + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &&mut self.0 } + | ------------ you could clone this value + +error[E0596]: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + --> $DIR/self-coercion-errors.rs:161:23 + | +LL | reuse Trait::* { &&mut self.0 } + | ^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | reuse Trait::mut * { &&mut self.0 } + | +++ + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | ^^^^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self.0` as mutable, as it is behind a `&` reference + --> $DIR/self-coercion-errors.rs:161:23 + | +LL | reuse Trait::* { &&mut self.0 } + | ^^^^^^^^^^^ `self` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - reuse Trait::* { &&mut self.0 } +LL + reuse Trait::&mut self { &&mut self.0 } + | + +error[E0507]: cannot move out of an `Arc` + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value + +error[E0596]: cannot borrow data in an `Arc` as mutable + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | ^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc>` + +error[E0507]: cannot move out of an `Arc` + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `Box`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::r#box` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:14:14 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value +help: you could `clone` the value and consume it, if the `F: Clone` trait bound could be satisfied + | +LL | reuse Trait::* { as Clone>::clone(&self.0) } + | ++++++++++++++++++++++++++ + +help: consider annotating `F` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct F; + | + +error[E0507]: cannot move out of dereference of `Pin>` + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value + +error[E0596]: cannot borrow data in dereference of `Pin>` as mutable + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | ^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin>` + +error[E0507]: cannot move out of dereference of `Pin>` + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `Pin>`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::pin_box` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:17:16 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value +help: you could `clone` the value and consume it, if the `F: Clone` trait bound could be satisfied + | +LL | reuse Trait::* { > as Clone>::clone(&self.0) } + | +++++++++++++++++++++++++++++++ + +help: consider annotating `F` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct F; + | + +error: aborting due to 99 previous errors + +Some errors have detailed explanations: E0277, E0308, E0507, E0596. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/self-coercion-static-free.rs b/tests/ui/delegation/self-coercion-static-free.rs new file mode 100644 index 0000000000000..f61d4247a1d94 --- /dev/null +++ b/tests/ui/delegation/self-coercion-static-free.rs @@ -0,0 +1,52 @@ +// Test that we do not adjust first argument type to first parameter type +// in delegations to static associated functions (we only do it for methods). +// Also test that we do not adjust first arg. type in delegations to free function. + +#![feature(fn_delegation)] + +pub trait Trait: Sized { + fn static_self() -> F { F } + + fn static_value(_: Self) -> i32 { 1 } + fn static_mut_ref(_: &mut Self) -> i32 { 2 } + fn static_ref(_: &Self) -> i32 { 3 } +} + +#[derive(Default)] +struct F; +impl Trait for F {} + +struct S(F); + +impl Trait for S { + reuse ::{static_value, static_mut_ref, static_ref} { + let _ = self; + S::static_self() + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + } +} + +struct S1(Box>>>>>); + +impl Trait for S1 { + reuse ::{static_value, static_mut_ref, static_ref} { + let _ = self; + S1::static_self() + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + } +} + +mod to_reuse { + use super::Trait; + pub fn value(_: impl Trait) -> i32 { 1 } + pub fn mut_ref(_: &mut impl Trait) -> i32 { 2 } + pub fn r#ref(_: &impl Trait) -> i32 { 3 } +} + +reuse to_reuse::{value, mut_ref, r#ref} { F } +//~^ ERROR: mismatched types +//~| ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/delegation/self-coercion-static-free.stderr b/tests/ui/delegation/self-coercion-static-free.stderr new file mode 100644 index 0000000000000..5f78272815aa5 --- /dev/null +++ b/tests/ui/delegation/self-coercion-static-free.stderr @@ -0,0 +1,87 @@ +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:24:9 + | +LL | S::static_self() + | ^^^^^^^^^^^^^^^^ expected `&mut F`, found `F` + | +help: consider mutably borrowing here + | +LL | &mut S::static_self() + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:24:9 + | +LL | S::static_self() + | ^^^^^^^^^^^^^^^^ expected `&F`, found `F` + | +help: consider borrowing here + | +LL | &S::static_self() + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:35:9 + | +LL | S1::static_self() + | ^^^^^^^^^^^^^^^^^ expected `&mut F`, found `F` + | +help: consider mutably borrowing here + | +LL | &mut S1::static_self() + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:35:9 + | +LL | S1::static_self() + | ^^^^^^^^^^^^^^^^^ expected `&F`, found `F` + | +help: consider borrowing here + | +LL | &S1::static_self() + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:48:43 + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { F } + | ------- ^ expected `&mut _`, found `F` + | | + | arguments to this function are incorrect + | + = note: expected mutable reference `&mut _` + found struct `F` +note: function defined here + --> $DIR/self-coercion-static-free.rs:44:12 + | +LL | pub fn mut_ref(_: &mut impl Trait) -> i32 { 2 } + | ^^^^^^^ ------------------ +help: consider mutably borrowing here + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { &mut F } + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:48:43 + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { F } + | ----- ^ expected `&_`, found `F` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found struct `F` +note: function defined here + --> $DIR/self-coercion-static-free.rs:45:12 + | +LL | pub fn r#ref(_: &impl Trait) -> i32 { 3 } + | ^^^^^ -------------- +help: consider borrowing here + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { &F } + | + + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/self-coercion.rs b/tests/ui/delegation/self-coercion.rs index e6c71ec1e257b..21b2b0cd2f56b 100644 --- a/tests/ui/delegation/self-coercion.rs +++ b/tests/ui/delegation/self-coercion.rs @@ -2,12 +2,13 @@ #![feature(fn_delegation)] -trait Trait : Sized { +trait Trait: Sized { fn by_value(self) -> i32 { 1 } fn by_mut_ref(&mut self) -> i32 { 2 } fn by_ref(&self) -> i32 { 3 } } +#[derive(Default)] struct F; impl Trait for F {} @@ -17,9 +18,20 @@ impl Trait for S { reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 } } +struct S1(Box>>>>>); + +impl Trait for S1 { + reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 } +} + fn main() { let mut s = S(F); assert_eq!(s.by_ref(), 3); assert_eq!(s.by_mut_ref(), 2); assert_eq!(s.by_value(), 1); + + let mut s = S1(Default::default()); + assert_eq!(s.by_ref(), 3); + assert_eq!(s.by_mut_ref(), 2); + assert_eq!(s.by_value(), 1); } diff --git a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs b/tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs deleted file mode 100644 index aad697c8c229e..0000000000000 --- a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(fn_delegation)] -#![feature(type_info)] - -use std::mem::type_info::Trait; - -impl Trait { -//~^ ERROR: cannot define inherent `impl` for a type outside of the crate where the type is defined - reuse None::<&()>; - //~^ ERROR: expected function, found unit variant `None` -} - -fn foo() {} - -reuse foo::<&&&&&&&&&&()> as foo1; -reuse foo::<&std::borrow::Cow<'_, &()>> as foo2; - -fn main() {} diff --git a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr b/tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr deleted file mode 100644 index dbe4823ae0c97..0000000000000 --- a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0423]: expected function, found unit variant `None` - --> $DIR/wrong-lifetime-rib-ice-156342.rs:8:11 - | -LL | reuse None::<&()>; - | ^^^^^^^^^^^ not a function - -error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined - --> $DIR/wrong-lifetime-rib-ice-156342.rs:6:1 - | -LL | impl Trait { - | ^^^^^^^^^^ impl for type defined outside of crate - | - = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it - = note: for more details about the orphan rules, see - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0116, E0423. -For more information about an error, try `rustc --explain E0116`. diff --git a/tests/ui/delegation/wrong-lifetime-rib.rs b/tests/ui/delegation/wrong-lifetime-rib.rs new file mode 100644 index 0000000000000..01645f20bf7b1 --- /dev/null +++ b/tests/ui/delegation/wrong-lifetime-rib.rs @@ -0,0 +1,42 @@ +//@ edition:2024 + +#![feature(fn_delegation)] +#![feature(type_info)] + +mod ice_156342 { + use std::mem::type_info::Trait; + + impl Trait { + //~^ ERROR: cannot define inherent `impl` for a type outside of the crate where the type is defined + reuse None::<&()>; + //~^ ERROR: expected function, found unit variant `None` + } + + fn foo() {} + + reuse foo::<&&&&&&&&&&()> as foo1; + reuse foo::<&std::borrow::Cow<'_, &()>> as foo2; +} + +mod ice_156758 { + trait X {} + type Project = (); + type Ty = (); + + impl X { //~ ERROR: expected a type, found a trait + reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; + //~^ ERROR: ambiguous associated type + } +} + +mod ice_156806 { + trait X {} + + impl X { //~ ERROR: expected a type, found a trait + reuse Iterator::fold { + let _: &X; //~ ERROR: expected a type, found a trait + } + } +} + +fn main() {} diff --git a/tests/ui/delegation/wrong-lifetime-rib.stderr b/tests/ui/delegation/wrong-lifetime-rib.stderr new file mode 100644 index 0000000000000..0c4499eec192d --- /dev/null +++ b/tests/ui/delegation/wrong-lifetime-rib.stderr @@ -0,0 +1,72 @@ +error[E0423]: expected function, found unit variant `None` + --> $DIR/wrong-lifetime-rib.rs:11:15 + | +LL | reuse None::<&()>; + | ^^^^^^^^^^^ not a function + +error[E0782]: expected a type, found a trait + --> $DIR/wrong-lifetime-rib.rs:26:10 + | +LL | impl X { + | ^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | impl dyn X { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl X for /* Type */ { + | ++++++++++++++ + +error[E0782]: expected a type, found a trait + --> $DIR/wrong-lifetime-rib.rs:35:10 + | +LL | impl X { + | ^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | impl dyn X { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl X for /* Type */ { + | ++++++++++++++ + +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/wrong-lifetime-rib.rs:9:5 + | +LL | impl Trait { + | ^^^^^^^^^^ impl for type defined outside of crate + | + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see + +error[E0223]: ambiguous associated type + --> $DIR/wrong-lifetime-rib.rs:27:16 + | +LL | reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; + | ^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `Ty` implemented for `&()`, you could use the fully-qualified path + | +LL - reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; +LL + reuse<<<&() as Example>::Ty> :: Ty as Iterator>::next; + | + +error[E0782]: expected a type, found a trait + --> $DIR/wrong-lifetime-rib.rs:37:21 + | +LL | let _: &X; + | ^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | let _: &dyn X; + | +++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0116, E0223, E0423, E0782. +For more information about an error, try `rustc --explain E0116`. diff --git a/tests/ui/issues/issue-17337.rs b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs similarity index 81% rename from tests/ui/issues/issue-17337.rs rename to tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs index 193f89f837897..a4142ff701aec 100644 --- a/tests/ui/issues/issue-17337.rs +++ b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs @@ -1,3 +1,4 @@ +//! Regression test for . #![feature(staged_api)] #![deny(deprecated)] diff --git a/tests/ui/issues/issue-17337.stderr b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr similarity index 65% rename from tests/ui/issues/issue-17337.stderr rename to tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr index 8fbf6882699de..6c21b45dfd3cc 100644 --- a/tests/ui/issues/issue-17337.stderr +++ b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr @@ -1,11 +1,11 @@ error: use of deprecated method `Foo::foo`: text - --> $DIR/issue-17337.rs:16:6 + --> $DIR/deprecated-unstable-method-in-staged-api.rs:17:6 | LL | .foo(); | ^^^ | note: the lint level is defined here - --> $DIR/issue-17337.rs:2:9 + --> $DIR/deprecated-unstable-method-in-staged-api.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ diff --git a/tests/ui/derives/deriving-all-codegen.rs b/tests/ui/derives/deriving-all-codegen.rs index 9f21831960499..343d4095da470 100644 --- a/tests/ui/derives/deriving-all-codegen.rs +++ b/tests/ui/derives/deriving-all-codegen.rs @@ -235,3 +235,21 @@ struct FooCopyAndClone(i32); #[derive(Clone)] #[derive(Copy)] struct FooCloneAndCopy(i32); + +#[derive(PartialOrd, Ord)] +struct FooPartialOrdOrd(i32); + +#[derive(Ord, PartialOrd)] +struct FooOrdPartialOrd(i32); + +#[derive(Ord)] +#[derive(PartialOrd)] +struct FooOrdBeforePartialOrd(i32); + +// FIXME: this case should also have a trivial `PartialOrd` impl. +#[derive(PartialOrd)] +#[derive(Ord)] +struct FooPartialOrdBeforeOrd(i32); + +#[derive(PartialOrd, Ord)] +struct UnitStruct; diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 94e8b886436df..83a3bebceeb0c 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -151,11 +151,7 @@ impl ::core::cmp::PartialOrd for Point { #[inline] fn partial_cmp(&self, other: &Point) -> ::core::option::Option<::core::cmp::Ordering> { - match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) => - ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y), - cmp => cmp, - } + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -239,13 +235,7 @@ impl ::core::cmp::PartialOrd for PackedPoint { #[inline] fn partial_cmp(&self, other: &PackedPoint) -> ::core::option::Option<::core::cmp::Ordering> { - match ::core::cmp::PartialOrd::partial_cmp(&{ self.x }, &{ other.x }) - { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) => - ::core::cmp::PartialOrd::partial_cmp(&{ self.y }, - &{ other.y }), - cmp => cmp, - } + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -322,7 +312,7 @@ impl ::core::cmp::PartialOrd for TupleSingleField { #[inline] fn partial_cmp(&self, other: &TupleSingleField) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -397,7 +387,7 @@ impl ::core::cmp::PartialOrd for SingleField { #[inline] fn partial_cmp(&self, other: &SingleField) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.foo, &other.foo) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -502,47 +492,7 @@ impl ::core::cmp::PartialOrd for Big { #[inline] fn partial_cmp(&self, other: &Big) -> ::core::option::Option<::core::cmp::Ordering> { - match ::core::cmp::PartialOrd::partial_cmp(&self.b1, &other.b1) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) => - match ::core::cmp::PartialOrd::partial_cmp(&self.b2, - &other.b2) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b3, - &other.b3) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b4, - &other.b4) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b5, - &other.b5) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b6, - &other.b6) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b7, - &other.b7) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - ::core::cmp::PartialOrd::partial_cmp(&self.b8, &other.b8), - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - } + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -766,7 +716,7 @@ impl ::core::cmp::PartialOrd for Unsized { #[inline] fn partial_cmp(&self, other: &Unsized) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1138,10 +1088,7 @@ impl ::core::cmp::PartialOrd for Enum1 { #[inline] fn partial_cmp(&self, other: &Enum1) -> ::core::option::Option<::core::cmp::Ordering> { - match (self, other) { - (Enum1::Single { x: __self_0 }, Enum1::Single { x: __arg1_0 }) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - } + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1279,9 +1226,7 @@ impl ::core::cmp::PartialOrd for Fieldless { #[inline] fn partial_cmp(&self, other: &Fieldless) -> ::core::option::Option<::core::cmp::Ordering> { - let __self_discr = ::core::intrinsics::discriminant_value(self); - let __arg1_discr = ::core::intrinsics::discriminant_value(other); - ::core::cmp::PartialOrd::partial_cmp(&__self_discr, &__arg1_discr) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1393,23 +1338,7 @@ impl ::core::cmp::PartialOrd for Mixed { #[inline] fn partial_cmp(&self, other: &Mixed) -> ::core::option::Option<::core::cmp::Ordering> { - let __self_discr = ::core::intrinsics::discriminant_value(self); - let __arg1_discr = ::core::intrinsics::discriminant_value(other); - match (self, other) { - (Mixed::R(__self_0), Mixed::R(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S { - d1: __arg1_0, d2: __arg1_1 }) => - match ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0) - { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => ::core::cmp::PartialOrd::partial_cmp(__self_1, __arg1_1), - cmp => cmp, - }, - _ => - ::core::cmp::PartialOrd::partial_cmp(&__self_discr, - &__arg1_discr), - } + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1591,19 +1520,7 @@ impl ::core::cmp::PartialOrd for Fielded { #[inline] fn partial_cmp(&self, other: &Fielded) -> ::core::option::Option<::core::cmp::Ordering> { - let __self_discr = ::core::intrinsics::discriminant_value(self); - let __arg1_discr = ::core::intrinsics::discriminant_value(other); - match (self, other) { - (Fielded::X(__self_0), Fielded::X(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - _ => - ::core::cmp::PartialOrd::partial_cmp(&__self_discr, - &__arg1_discr), - } + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1849,3 +1766,89 @@ impl ::core::clone::Clone for FooCloneAndCopy { FooCloneAndCopy(::core::clone::Clone::clone(&self.0)) } } + +struct FooPartialOrdOrd(i32); +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooPartialOrdOrd { + #[inline] + fn partial_cmp(&self, other: &FooPartialOrdOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for FooPartialOrdOrd { + #[inline] + fn cmp(&self, other: &FooPartialOrdOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} + +struct FooOrdPartialOrd(i32); +#[automatically_derived] +impl ::core::cmp::Ord for FooOrdPartialOrd { + #[inline] + fn cmp(&self, other: &FooOrdPartialOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooOrdPartialOrd { + #[inline] + fn partial_cmp(&self, other: &FooOrdPartialOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} + +struct FooOrdBeforePartialOrd(i32); +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooOrdBeforePartialOrd { + #[inline] + fn partial_cmp(&self, other: &FooOrdBeforePartialOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for FooOrdBeforePartialOrd { + #[inline] + fn cmp(&self, other: &FooOrdBeforePartialOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} + +// FIXME: this case should also have a trivial `PartialOrd` impl. +struct FooPartialOrdBeforeOrd(i32); +#[automatically_derived] +impl ::core::cmp::Ord for FooPartialOrdBeforeOrd { + #[inline] + fn cmp(&self, other: &FooPartialOrdBeforeOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooPartialOrdBeforeOrd { + #[inline] + fn partial_cmp(&self, other: &FooPartialOrdBeforeOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +struct UnitStruct; +#[automatically_derived] +impl ::core::cmp::PartialOrd for UnitStruct { + #[inline] + fn partial_cmp(&self, other: &UnitStruct) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ordering::Equal) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for UnitStruct { + #[inline] + fn cmp(&self, other: &UnitStruct) -> ::core::cmp::Ordering { + ::core::cmp::Ordering::Equal + } +} diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.rs b/tests/ui/derives/deriving-with-repr-packed-move-errors.rs index 17aa750332c40..6ff81739cd9a8 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.rs +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.rs @@ -18,8 +18,6 @@ struct StructA(String); //~| ERROR: cannot move out of a shared reference [E0507] //~| ERROR: cannot move out of a shared reference [E0507] //~| ERROR: cannot move out of a shared reference [E0507] -//~| ERROR: cannot move out of a shared reference [E0507] -//~| ERROR: cannot move out of a shared reference [E0507] // Unrelated impl: additinal diagnostic should NOT be emitted diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr index e857b308d531f..a40068171212e 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr @@ -29,27 +29,6 @@ LL | struct StructA(String); = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0507]: cannot move out of a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 - | -LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] - | ---------- in this derive macro expansion -LL | struct StructA(String); - | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait - | - = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - -error[E0507]: cannot move out of a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 - | -LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] - | ---------- in this derive macro expansion -LL | struct StructA(String); - | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait - | - = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | @@ -92,7 +71,7 @@ LL | struct StructA(String); = note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:28:9 + --> $DIR/deriving-with-repr-packed-move-errors.rs:26:9 | LL | self.0 | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -103,7 +82,7 @@ LL | self.0.clone() | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:38:20 + --> $DIR/deriving-with-repr-packed-move-errors.rs:36:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -114,7 +93,7 @@ LL | let x = &{ self.0.clone() }; | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:45:12 + --> $DIR/deriving-with-repr-packed-move-errors.rs:43:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -125,7 +104,7 @@ LL | ({ self.0.clone() }) == ({ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:45:28 + --> $DIR/deriving-with-repr-packed-move-errors.rs:43:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -136,7 +115,7 @@ LL | ({ self.0 }) == ({ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:53:36 + --> $DIR/deriving-with-repr-packed-move-errors.rs:51:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -147,7 +126,7 @@ LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:53:49 + --> $DIR/deriving-with-repr-packed-move-errors.rs:51:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -158,7 +137,7 @@ LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:68:20 + --> $DIR/deriving-with-repr-packed-move-errors.rs:66:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -169,7 +148,7 @@ LL | let x = &{ self.0.clone() }; | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:75:12 + --> $DIR/deriving-with-repr-packed-move-errors.rs:73:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -180,7 +159,7 @@ LL | ({ self.0.clone() }) == ({ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:75:28 + --> $DIR/deriving-with-repr-packed-move-errors.rs:73:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -191,7 +170,7 @@ LL | ({ self.0 }) == ({ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:83:36 + --> $DIR/deriving-with-repr-packed-move-errors.rs:81:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -202,7 +181,7 @@ LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:83:49 + --> $DIR/deriving-with-repr-packed-move-errors.rs:81:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -213,7 +192,7 @@ LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `arg` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:92:5 + --> $DIR/deriving-with-repr-packed-move-errors.rs:90:5 | LL | arg.0 | ^^^^^ move occurs because `arg.0` has type `String`, which does not implement the `Copy` trait @@ -223,6 +202,6 @@ help: consider cloning the value if the performance cost is acceptable LL | arg.0.clone() | ++++++++ -error: aborting due to 21 previous errors +error: aborting due to 19 previous errors For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs b/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs new file mode 100644 index 0000000000000..36fe0795c33a7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs @@ -0,0 +1,37 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, quote}; + +#[proc_macro_attribute] +pub fn repro(_args: TokenStream, input: TokenStream) -> TokenStream { + // Parse input that looks like `fn f(arg: &mut Arg);` + let mut input = input.into_iter(); + assert_eq!(input.next().unwrap().to_string(), "fn"); + assert_eq!(input.next().unwrap().to_string(), "f"); + let TokenTree::Group(group) = input.next().unwrap() else { unreachable!() }; + let mut input = group.stream().into_iter(); + assert_eq!(input.next().unwrap().to_string(), "arg"); + assert_eq!(input.next().unwrap().to_string(), ":"); + let arg: TokenStream = input.collect(); + + // Emit output: + quote! { + const _: fn() = { + #[diagnostic::on_unimplemented( + message = "mutable reference to C++ type requires a pin -- use Pin<&mut Arg>", + label = "use `Pin<&mut Arg>`" + )] + trait ReferenceToUnpin_Arg { + fn check_unpin() {} + } + #[diagnostic::do_not_recommend] + impl< + 'a, + T: ?::core::marker::Sized + ::core::marker::Unpin, + > ReferenceToUnpin_Arg for &'a mut T {} + <$arg as ReferenceToUnpin_Arg>::check_unpin + }; + } +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr new file mode 100644 index 0000000000000..1236ac5dfe1c2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: mutable reference to C++ type requires a pin -- use Pin<&mut Arg> + --> $DIR/proc-macro-span.rs:15:11 + | +LL | fn f(arg: &mut Arg); + | ^^^^^^^^ use `Pin<&mut Arg>` + | + = help: the trait `ReferenceToUnpin_Arg` is not implemented for `&mut Arg` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr new file mode 100644 index 0000000000000..1236ac5dfe1c2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr @@ -0,0 +1,11 @@ +error[E0277]: mutable reference to C++ type requires a pin -- use Pin<&mut Arg> + --> $DIR/proc-macro-span.rs:15:11 + | +LL | fn f(arg: &mut Arg); + | ^^^^^^^^ use `Pin<&mut Arg>` + | + = help: the trait `ReferenceToUnpin_Arg` is not implemented for `&mut Arg` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs new file mode 100644 index 0000000000000..3d133813f6e05 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs @@ -0,0 +1,16 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ compile-flags: --crate-type=lib +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@ proc-macro: proc_macro_repro.rs + +// Regression test for https://github.com/rust-lang/rust/issues/156759. + +extern crate proc_macro_repro; + +struct Arg(std::marker::PhantomPinned); + +#[proc_macro_repro::repro] +fn f(arg: &mut Arg); +//~^ ERROR mutable reference to C++ type requires a pin diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index 983f871dde109..b193d4be83c8d 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -16,18 +16,18 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] | = help: you can print empty braces by escaping them -warning: format specifiers are not permitted in diagnostic attributes - --> $DIR/broken_format.rs:10:50 - | -LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^ remove this format specifier - warning: indexed format arguments are not permitted in diagnostic attributes --> $DIR/broken_format.rs:10:49 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] | ^ remove this format argument +warning: format specifiers are not permitted in diagnostic attributes + --> $DIR/broken_format.rs:10:50 + | +LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] + | ^ remove this format specifier + warning: format specifiers are not permitted in diagnostic attributes --> $DIR/broken_format.rs:15:53 | diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs index e3c38c7f55c95..aa4e14fb04f2f 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -35,13 +35,16 @@ impl Bar for i32 {} //~|WARN there is no parameter `r#struct` on trait `Baz` //~|WARN there is no parameter `r#enum` on trait `Baz` //~|WARN there is no parameter `union` on trait `Baz` - label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" + label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" //~^WARN there is no parameter `float` on trait `Baz` //~|WARN there is no parameter `_Self` on trait `Baz` //~|WARN there is no parameter `crate_local` on trait `Baz` - //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `ItemContext` on trait `Baz` //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN format specifiers are not permitted in diagnostic attributes + //~|WARN format specifiers are not permitted in diagnostic attributes )] trait Baz {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index 9ed72d334b271..7ab7f25ee6964 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -66,7 +66,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{ warning: there is no parameter `float` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:15 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -74,7 +74,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" warning: there is no parameter `_Self` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:22 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -82,32 +82,40 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" warning: there is no parameter `crate_local` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:29 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument -warning: there is no parameter `Trait` on trait `Baz` +warning: there is no parameter `This` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:42 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:48 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^^^^^^^ +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:62 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:61 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^ +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `This` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:72 + | +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -152,8 +160,20 @@ LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl | = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default +warning: format specifiers are not permitted in diagnostic attributes + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:65 + | +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^^ remove this format specifier + +warning: format specifiers are not permitted in diagnostic attributes + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:76 + | +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^^^^^^ remove this format specifier + error[E0277]: trait has `()` and `i32` as params - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:56:15 | LL | takes_foo(()); | --------- ^^ trait has `()` and `i32` as params @@ -168,13 +188,13 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:48:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:51:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^^^^^^ required by this bound in `takes_foo` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:55:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:15 | LL | takes_bar(()); | --------- ^^ the trait `Bar` is not implemented for `()` @@ -187,31 +207,31 @@ help: the trait `Bar` is implemented for `i32` LL | impl Bar for i32 {} | ^^^^^^^^^^^^^^^^ note: required by a bound in `takes_bar` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:49:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:22 | LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union} - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:15 | LL | takes_baz(()); - | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This} + | --------- ^^ {float}{_Self}{crate_local}{This}{ItemContext}{This}{This} | | | required by a bound introduced by this call | = help: the trait `Baz` is not implemented for `()` help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:49:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `takes_baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:22 | LL | fn takes_baz(_: impl Baz) {} | ^^^ required by this bound in `takes_baz` -error: aborting due to 3 previous errors; 19 warnings emitted +error: aborting due to 3 previous errors; 22 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-17216.rs b/tests/ui/drop/drop-in-for-loop-destructure.rs similarity index 83% rename from tests/ui/issues/issue-17216.rs rename to tests/ui/drop/drop-in-for-loop-destructure.rs index 31b16ef3a2f7b..2c277777eaf6a 100644 --- a/tests/ui/issues/issue-17216.rs +++ b/tests/ui/drop/drop-in-for-loop-destructure.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-pass #![allow(unused_variables)] struct Leak<'a> { diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs deleted file mode 100644 index 181f9a210003f..0000000000000 --- a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ only-aarch64 -#[inline(always)] -//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` -#[target_feature(enable="fp16")] -fn test() { - -} - -fn main() { } diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr deleted file mode 100644 index de54844bc291d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/feature-gate-target-feature-inline-always.rs:2:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #145574 for more information - = help: add `#![feature(target_feature_inline_always)]` 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: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/float/f32-into-f32.next-solver.fixed b/tests/ui/float/f32-into-f32.next-solver.fixed index a8d56f4428b91..6bc04ff757eef 100644 --- a/tests/ui/float/f32-into-f32.next-solver.fixed +++ b/tests/ui/float/f32-into-f32.next-solver.fixed @@ -15,6 +15,9 @@ fn main() { foo(1e5_f32); //~^ WARN falling back to `f32` //~| WARN this was previously accepted + foo(0_f32); + //~^ WARN falling back to `f32` + //~| WARN this was previously accepted foo(4f32); // no warning let x = -4.0_f32; //~^ WARN falling back to `f32` diff --git a/tests/ui/float/f32-into-f32.next-solver.stderr b/tests/ui/float/f32-into-f32.next-solver.stderr index fc88f9d2c7f3c..1384f73f09689 100644 --- a/tests/ui/float/f32-into-f32.next-solver.stderr +++ b/tests/ui/float/f32-into-f32.next-solver.stderr @@ -27,7 +27,16 @@ LL | foo(1e5); = note: for more information, see issue #154024 warning: falling back to `f32` as the trait bound `f32: From` is not satisfied - --> $DIR/f32-into-f32.rs:19:14 + --> $DIR/f32-into-f32.rs:18:9 + | +LL | foo(0.); + | ^^ help: explicitly specify the type as `f32`: `0_f32` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #154024 + +warning: falling back to `f32` as the trait bound `f32: From` is not satisfied + --> $DIR/f32-into-f32.rs:22:14 | LL | let x = -4.0; | ^^^ help: explicitly specify the type as `f32`: `4.0_f32` @@ -35,5 +44,5 @@ LL | let x = -4.0; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #154024 -warning: 4 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/float/f32-into-f32.old-solver.fixed b/tests/ui/float/f32-into-f32.old-solver.fixed index a8d56f4428b91..6bc04ff757eef 100644 --- a/tests/ui/float/f32-into-f32.old-solver.fixed +++ b/tests/ui/float/f32-into-f32.old-solver.fixed @@ -15,6 +15,9 @@ fn main() { foo(1e5_f32); //~^ WARN falling back to `f32` //~| WARN this was previously accepted + foo(0_f32); + //~^ WARN falling back to `f32` + //~| WARN this was previously accepted foo(4f32); // no warning let x = -4.0_f32; //~^ WARN falling back to `f32` diff --git a/tests/ui/float/f32-into-f32.old-solver.stderr b/tests/ui/float/f32-into-f32.old-solver.stderr index fc88f9d2c7f3c..1384f73f09689 100644 --- a/tests/ui/float/f32-into-f32.old-solver.stderr +++ b/tests/ui/float/f32-into-f32.old-solver.stderr @@ -27,7 +27,16 @@ LL | foo(1e5); = note: for more information, see issue #154024 warning: falling back to `f32` as the trait bound `f32: From` is not satisfied - --> $DIR/f32-into-f32.rs:19:14 + --> $DIR/f32-into-f32.rs:18:9 + | +LL | foo(0.); + | ^^ help: explicitly specify the type as `f32`: `0_f32` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #154024 + +warning: falling back to `f32` as the trait bound `f32: From` is not satisfied + --> $DIR/f32-into-f32.rs:22:14 | LL | let x = -4.0; | ^^^ help: explicitly specify the type as `f32`: `4.0_f32` @@ -35,5 +44,5 @@ LL | let x = -4.0; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #154024 -warning: 4 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/float/f32-into-f32.rs b/tests/ui/float/f32-into-f32.rs index b55023453b7e0..3dbdd20bd3098 100644 --- a/tests/ui/float/f32-into-f32.rs +++ b/tests/ui/float/f32-into-f32.rs @@ -15,6 +15,9 @@ fn main() { foo(1e5); //~^ WARN falling back to `f32` //~| WARN this was previously accepted + foo(0.); + //~^ WARN falling back to `f32` + //~| WARN this was previously accepted foo(4f32); // no warning let x = -4.0; //~^ WARN falling back to `f32` diff --git a/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed new file mode 100644 index 0000000000000..d8a7858e7659b --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed @@ -0,0 +1,28 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable +//@ edition: 2024 + +// Verify that the suggestion produced by `impl_trait_redundant_captures` +// removes the adjacent `+` joiner along with `use<...>`, instead of leaving +// behind a stray `+` that fails to compile. Regression test for +// https://github.com/rust-lang/rust/issues/143216. + +#![allow(unused)] +#![deny(impl_trait_redundant_captures)] + +// `use<>` at the end of the bound list: the suggestion must remove the +// preceding `+`. +fn end_position() -> impl Sized {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` at the start of the bound list: the suggestion must remove the +// following `+`. +fn start_position() -> impl Sized {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` in the middle of the bound list: the suggestion must remove +// exactly one `+`, keeping the other to join the remaining bounds. +fn middle_position() -> impl Sized + Send {} +//~^ ERROR all possible in-scope parameters are already captured + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs new file mode 100644 index 0000000000000..c6b2d4cba1c25 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs @@ -0,0 +1,28 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable +//@ edition: 2024 + +// Verify that the suggestion produced by `impl_trait_redundant_captures` +// removes the adjacent `+` joiner along with `use<...>`, instead of leaving +// behind a stray `+` that fails to compile. Regression test for +// https://github.com/rust-lang/rust/issues/143216. + +#![allow(unused)] +#![deny(impl_trait_redundant_captures)] + +// `use<>` at the end of the bound list: the suggestion must remove the +// preceding `+`. +fn end_position() -> impl Sized + use<> {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` at the start of the bound list: the suggestion must remove the +// following `+`. +fn start_position() -> impl use<> + Sized {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` in the middle of the bound list: the suggestion must remove +// exactly one `+`, keeping the other to join the remaining bounds. +fn middle_position() -> impl Sized + use<> + Send {} +//~^ ERROR all possible in-scope parameters are already captured + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr new file mode 100644 index 0000000000000..f07fc4905680b --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr @@ -0,0 +1,32 @@ +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant-machine-applicable.rs:15:22 + | +LL | fn end_position() -> impl Sized + use<> {} + | ^^^^^^^^^^-------- + | | + | help: remove the `use<...>` syntax + | +note: the lint level is defined here + --> $DIR/redundant-machine-applicable.rs:11:9 + | +LL | #![deny(impl_trait_redundant_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant-machine-applicable.rs:20:24 + | +LL | fn start_position() -> impl use<> + Sized {} + | ^^^^^--------^^^^^ + | | + | help: remove the `use<...>` syntax + +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant-machine-applicable.rs:25:25 + | +LL | fn middle_position() -> impl Sized + use<> + Send {} + | ^^^^^^^^^^^^^--------^^^^ + | | + | help: remove the `use<...>` syntax + +error: aborting due to 3 previous errors + diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr index c9f84d360e3c6..62724fc01976a 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.stderr +++ b/tests/ui/impl-trait/precise-capturing/redundant.stderr @@ -2,9 +2,9 @@ error: all possible in-scope parameters are already captured, so `use<...>` synt --> $DIR/redundant.rs:5:19 | LL | fn hello<'a>() -> impl Sized + use<'a> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------- + | | + | help: remove the `use<...>` syntax | note: the lint level is defined here --> $DIR/redundant.rs:3:9 @@ -16,25 +16,25 @@ error: all possible in-scope parameters are already captured, so `use<...>` synt --> $DIR/redundant.rs:10:27 | LL | fn inherent(&self) -> impl Sized + use<'_> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------- + | | + | help: remove the `use<...>` syntax error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:15:22 | LL | fn in_trait() -> impl Sized + use<'a, Self>; - | ^^^^^^^^^^^^^------------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------------- + | | + | help: remove the `use<...>` syntax error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:19:22 | LL | fn in_trait() -> impl Sized + use<'a> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------- + | | + | help: remove the `use<...>` syntax error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-17551.rs b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs similarity index 69% rename from tests/ui/issues/issue-17551.rs rename to tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs index a65957ce074ef..3b6c20fe5bfbb 100644 --- a/tests/ui/issues/issue-17551.rs +++ b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for . + use std::marker; struct B(marker::PhantomData); diff --git a/tests/ui/issues/issue-17551.stderr b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr similarity index 90% rename from tests/ui/issues/issue-17551.stderr rename to tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr index b9cb76fc298d9..18c7f669fae7d 100644 --- a/tests/ui/issues/issue-17551.stderr +++ b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed for `B<_>` - --> $DIR/issue-17551.rs:6:9 + --> $DIR/unconstrained-type-in-closure.rs:8:9 | LL | let foo = B(marker::PhantomData); | ^^^ ------------------- type must be known at this point diff --git a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs b/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs deleted file mode 100644 index b8deb6ab3c64a..0000000000000 --- a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Regression test for #137588. -// The compiler used to ICE when emitting a `fuzzy_provenance_casts` lint -// diagnostic for code with an inner attribute spanning the entire file, -// causing `draw_code_line` to panic on an empty `file_lines` from a dummy span. - -//@ edition:2024 -//@ compile-flags: -Wfuzzy-provenance-casts - -#![feature(strict_provenance_lints)] -//~^ ERROR too many leading `super` keywords [E0433] -//~| ERROR cannot find type `Ts` in this scope [E0425] -//~| ERROR `#[prelude_import]` is for use by rustc only [E0658] -//~| WARN strict provenance disallows casting integer `usize` to pointer `*const u32` -#![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] -//~^ ERROR use of unstable library feature `contracts` [E0658] -//~| ERROR inner macro attributes are unstable [E0658] -//~| ERROR cannot find type `Stars` in this scope [E0433] - -pub(super) fn foo() -> *const Ts { - unsafe { - let p2 = 0x52 as *const u32; - } -} -//~^ ERROR `main` function not found in crate diff --git a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr b/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr deleted file mode 100644 index 342f39d2450d8..0000000000000 --- a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr +++ /dev/null @@ -1,93 +0,0 @@ -error[E0658]: use of unstable library feature `contracts` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:4 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #128044 for more information - = help: add `#![feature(contracts)]` 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]: inner macro attributes are unstable - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:4 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #54726 for more information - = help: add `#![feature(custom_inner_attributes)]` 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[E0433]: too many leading `super` keywords - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ there are too many leading `super` keywords - -error[E0425]: cannot find type `Ts` in this scope - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ not found in this scope - -error[E0658]: `#[prelude_import]` is for use by rustc only - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ - | - = help: add `#![feature(prelude_import)]` 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[E0601]: `main` function not found in crate `ice_fuzzy_provenance_casts_with_inner_attr` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:23:2 - | -LL | } - | ^ consider adding a `main` function to `$DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs` - -error[E0433]: cannot find type `Stars` in this scope - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:50 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^ use of undeclared type `Stars` - -warning: strict provenance disallows casting integer `usize` to pointer `*const u32` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ - | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead - = note: requested on the command line with `-W fuzzy-provenance-casts` -help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address - | -LL - #![feature(strict_provenance_lints)] -LL - -LL - -LL - -LL - -LL - #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] -LL - -LL - -LL - -LL - -LL - pub(super) fn foo() -> *const Ts { -LL - unsafe { -LL - let p2 = 0x52 as *const u32; -LL - } -LL - } -LL + (...).with_addr() - | - -error: aborting due to 7 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0425, E0433, E0601, E0658. -For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.rs b/tests/ui/lint/lint-strict-provenance-macro-casts.rs new file mode 100644 index 0000000000000..6d1c02c91fdbd --- /dev/null +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.rs @@ -0,0 +1,33 @@ +#![feature(strict_provenance_lints)] +#![deny(lossy_provenance_casts)] +#![deny(fuzzy_provenance_casts)] + +macro_rules! cast { + ($e:expr, $t:ty) => { + $e as $t + //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~| ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + }; +} + +macro_rules! p2i { + ($e:expr) => { $e as usize }; + //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +} + +macro_rules! i2p { + ($e:expr) => { $e as *const () }; + //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const ()` +} + +fn main() { + let ptr = &0u8 as *const u8; + let _addr = cast!(ptr, usize); + + let _ptr = cast!(0usize, *const u8); + + let x = 1u8; + p2i!(&raw const x); + + i2p!(0x42); +} diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr new file mode 100644 index 0000000000000..a7ec3d56965f3 --- /dev/null +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr @@ -0,0 +1,60 @@ +error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 + | +LL | $e as $t + | ^^^^^^^^ +... +LL | let _addr = cast!(ptr, usize); + | ----------------- in this macro invocation + | + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead +note: the lint level is defined here + --> $DIR/lint-strict-provenance-macro-casts.rs:2:9 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: strict provenance disallows casting integer `usize` to pointer `*const u8` + --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 + | +LL | $e as $t + | ^^^^^^^^ +... +LL | let _ptr = cast!(0usize, *const u8); + | ------------------------ in this macro invocation + | + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead +note: the lint level is defined here + --> $DIR/lint-strict-provenance-macro-casts.rs:3:9 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + --> $DIR/lint-strict-provenance-macro-casts.rs:14:20 + | +LL | ($e:expr) => { $e as usize }; + | ^^^^^^^^^^^ +... +LL | p2i!(&raw const x); + | ------------------ in this macro invocation + | + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = note: this error originates in the macro `p2i` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: strict provenance disallows casting integer `usize` to pointer `*const ()` + --> $DIR/lint-strict-provenance-macro-casts.rs:19:20 + | +LL | ($e:expr) => { $e as *const () }; + | ^^^^^^^^^^^^^^^ +... +LL | i2p!(0x42); + | ---------- in this macro invocation + | + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = note: this error originates in the macro `i2p` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed new file mode 100644 index 0000000000000..9e188cfdcc0e3 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed @@ -0,0 +1,19 @@ +//@ check-pass +//@ run-rustfix + +#![warn(unused)] +#![allow(dead_code)] + +struct Foo { + r#move: u32 +} + +fn main() { + let y = Foo { r#move: 3 }; + + let _ = match y { + Foo { r#move: _ } => 0 //~ WARNING unused variable: `r#move` + //~| HELP try ignoring the field + //~| SUGGESTION r#move: _ + }; +} diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs new file mode 100644 index 0000000000000..514ba4f7df418 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ run-rustfix + +#![warn(unused)] +#![allow(dead_code)] + +struct Foo { + r#move: u32 +} + +fn main() { + let y = Foo { r#move: 3 }; + + let _ = match y { + Foo { r#move } => 0 //~ WARNING unused variable: `r#move` + //~| HELP try ignoring the field + //~| SUGGESTION r#move: _ + }; +} diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr new file mode 100644 index 0000000000000..0f96605651616 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `r#move` + --> $DIR/unused-variable-with-raw-identifier-in-struct-pattern.rs:15:16 + | +LL | Foo { r#move } => 0 + | ^^^^^^ help: try ignoring the field: `r#move: _` + | +note: the lint level is defined here + --> $DIR/unused-variable-with-raw-identifier-in-struct-pattern.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + diff --git a/tests/ui/liveness/liveness-upvars.rs b/tests/ui/liveness/liveness-upvars.rs index 0e198f1dea10b..be58b48a40576 100644 --- a/tests/ui/liveness/liveness-upvars.rs +++ b/tests/ui/liveness/liveness-upvars.rs @@ -98,7 +98,7 @@ pub fn g(mut v: T) { } pub fn h() { - let mut z = T::default(); //~ WARN unused variable: `z` + let mut z = T::default(); let _ = move |b| { loop { if b { diff --git a/tests/ui/liveness/liveness-upvars.stderr b/tests/ui/liveness/liveness-upvars.stderr index 9f5a3de7365a3..96a922772c919 100644 --- a/tests/ui/liveness/liveness-upvars.stderr +++ b/tests/ui/liveness/liveness-upvars.stderr @@ -157,14 +157,6 @@ LL | z = T::default(); | = help: maybe it is overwritten before being read? -warning: unused variable: `z` - --> $DIR/liveness-upvars.rs:101:9 - | -LL | let mut z = T::default(); - | ^^^^^ help: if this is intentional, prefix it with an underscore: `_z` - | - = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` - warning: value captured by `state` is never read --> $DIR/liveness-upvars.rs:131:9 | @@ -206,5 +198,5 @@ LL | s = yield (); LL | s = 3; | ----- `s` is overwritten here before the previous value is read -warning: 25 warnings emitted +warning: 24 warnings emitted diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index fec991ec95b7b..fde68f8f760dd 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -10,14 +10,17 @@ #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] +#![feature(impl_restriction)] #![feature(macro_guard_matcher)] #![feature(more_qualified_paths)] #![feature(move_expr)] +#![feature(mut_restriction)] #![feature(never_patterns)] #![feature(specialization)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(unsafe_fields)] #![deny(unused_macros)] // These macros force the use of AST pretty-printing by converting the input to @@ -924,6 +927,65 @@ fn test_impl_restriction() { ); } +#[test] +fn test_mut_restriction() { + assert_eq!( + stringify!(pub struct Foo { pub mut(crate) x: u8, pub mut(self) unsafe y: u8 }), + "pub struct Foo { pub mut(crate) x: u8, pub mut(self) unsafe y: u8 }" + ); + assert_eq!( + stringify!(pub struct Foo { pub mut(super) x: u8, pub mut(in path::to) unsafe y: u8 }), + "pub struct Foo { pub mut(super) x: u8, pub mut(in path::to) unsafe y: u8 }" + ); + assert_eq!( + stringify!(pub struct Foo { pub mut(in crate::path::to) x: u8 }), + "pub struct Foo { pub mut(in crate::path::to) x: u8 }" + ); + + assert_eq!( + stringify!(pub struct Foo(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8)), + "pub struct Foo(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8)" + ); + assert_eq!( + stringify!(pub struct Foo(pub mut(in path::to) u8, pub mut(in crate::path::to) u8)), + "pub struct Foo(pub mut(in path::to) u8, pub mut(in crate::path::to) u8)" + ); + + assert_eq!( + stringify!(pub enum Foo { Var{ pub mut(crate) x: u8, pub mut(self) unsafe y: u8 } }), + "pub enum Foo { Var{ pub mut(crate) x: u8, pub mut(self) unsafe y: u8 } }" + ); + assert_eq!( + stringify!(pub enum Foo { Var{ pub mut(super) x: u8, pub mut(in path::to) y: u8 } }), + "pub enum Foo { Var{ pub mut(super) x: u8, pub mut(in path::to) y: u8 } }" + ); + assert_eq!( + stringify!(pub enum Foo { Var{ pub mut(in crate::path::to) x: u8 } }), + "pub enum Foo { Var{ pub mut(in crate::path::to) x: u8 } }" + ); + assert_eq!( + stringify!(pub enum Foo { Tup(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8) }), + "pub enum Foo { Tup(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8) }" + ); + assert_eq!( + stringify!(pub enum Foo { Tup(pub mut(in path::to) u8, pub mut(in crate::path::to) u8) }), + "pub enum Foo { Tup(pub mut(in path::to) u8, pub mut(in crate::path::to) u8) }" + ); + + assert_eq!( + stringify!(pub union Foo { x: pub mut(crate) u8, y: pub mut(self) unsafe u8 }), + "pub union Foo { x: pub mut(crate) u8, y: pub mut(self) unsafe u8 }" + ); + assert_eq!( + stringify!(pub union Foo { x: pub mut(super) u8, y: pub mut(in path::to) unsafe u8 }), + "pub union Foo { x: pub mut(super) u8, y: pub mut(in path::to) unsafe u8 }" + ); + assert_eq!( + stringify!(pub union Foo { x: pub mut(in crate::path::to) u8 }), + "pub union Foo { x: pub mut(in crate::path::to) u8 }" + ); +} + #[test] fn test_punct() { // For all these cases, we should preserve spaces between the tokens. diff --git a/tests/ui/issues/issue-46855.rs b/tests/ui/mir/dont-use-operand-as-place-for-zst.rs similarity index 84% rename from tests/ui/issues/issue-46855.rs rename to tests/ui/mir/dont-use-operand-as-place-for-zst.rs index acea242046fde..0d1d12d5b404c 100644 --- a/tests/ui/issues/issue-46855.rs +++ b/tests/ui/mir/dont-use-operand-as-place-for-zst.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![allow(dead_code)] //@ compile-flags: -Zmir-opt-level=1 diff --git a/tests/ui/issues/issue-50811.rs b/tests/ui/mir/validate-various-comparison-behavior.rs similarity index 95% rename from tests/ui/issues/issue-50811.rs rename to tests/ui/mir/validate-various-comparison-behavior.rs index aaf1c17f59b5f..c1aaa4c35ae15 100644 --- a/tests/ui/issues/issue-50811.rs +++ b/tests/ui/mir/validate-various-comparison-behavior.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![feature(test)] #![allow(invalid_nan_comparisons)] diff --git a/tests/ui/mut-restriction/feature-gate-mut-restriction.rs b/tests/ui/mut-restriction/feature-gate-mut-restriction.rs new file mode 100644 index 0000000000000..3ec3345cf72a9 --- /dev/null +++ b/tests/ui/mut-restriction/feature-gate-mut-restriction.rs @@ -0,0 +1,104 @@ +//@ revisions: with_gate without_gate +//@[with_gate] check-pass + +#![cfg_attr(with_gate, feature(mut_restriction))] +#![feature(unsafe_fields)] + +pub struct Foo { + pub mut(crate) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(self) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +pub struct TupFoo(pub mut(crate) i32, pub mut(self) i32); //[without_gate]~ ERROR `mut` restrictions are experimental +//[without_gate]~^ ERROR `mut` restrictions are experimental + +pub enum EnumFoo { + Var { + mut(self) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(self) i32, mut(crate) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental +} + +pub union UnionFoo { + pub mut(self) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +pub mod foo { + pub struct Bar { + pub mut(super) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(in crate::foo) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } + + pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + + pub enum EnumBar { + Var { + mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(in crate::foo) i32, mut(super) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + } + + pub union UnionBar { + pub mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } +} + +#[cfg(false)] +pub struct Baz { + pub mut(crate) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(self) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +#[cfg(false)] +pub struct TupBaz(pub mut(crate) i32, pub mut(self) i32); //[without_gate]~ ERROR `mut` restrictions are experimental +//[without_gate]~^ ERROR `mut` restrictions are experimental + +#[cfg(false)] +pub enum EnumBaz { + Var { + mut(self) x: i32, //[without_gate]~ ERROR `mut` restrictions + mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(self) i32, mut(crate) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental +} + +#[cfg(false)] +pub union UnionBaz { + pub mut(self) x: i32, //[without_gate]~ ERROR `mut` + pub mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +#[cfg(false)] +pub mod bar { + pub struct Bar { + pub mut(super) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(in crate::foo) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } + + pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + + pub enum EnumBar { + Var { + mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(in crate::foo) i32, mut(super) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + } + + pub union UnionBar { + pub mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } +} + +fn main() {} diff --git a/tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr b/tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr new file mode 100644 index 0000000000000..8f6193c030557 --- /dev/null +++ b/tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr @@ -0,0 +1,403 @@ +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:8:9 + | +LL | pub mut(crate) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:9:9 + | +LL | pub mut(self) unsafe y: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:12:23 + | +LL | pub struct TupFoo(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:12:43 + | +LL | pub struct TupFoo(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:17:9 + | +LL | mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:18:9 + | +LL | mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:20:9 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:20:24 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:25:9 + | +LL | pub mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:26:9 + | +LL | pub mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:31:13 + | +LL | pub mut(super) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:32:13 + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:35:27 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:35:47 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:40:13 + | +LL | mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:41:13 + | +LL | mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:43:13 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:43:37 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:48:13 + | +LL | pub mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:49:13 + | +LL | pub mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:55:9 + | +LL | pub mut(crate) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:56:9 + | +LL | pub mut(self) unsafe y: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:60:23 + | +LL | pub struct TupBaz(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:60:43 + | +LL | pub struct TupBaz(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:66:9 + | +LL | mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:67:9 + | +LL | mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:69:9 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:69:24 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:75:9 + | +LL | pub mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:76:9 + | +LL | pub mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:82:13 + | +LL | pub mut(super) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:83:13 + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:86:27 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:86:47 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:91:13 + | +LL | mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:92:13 + | +LL | mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:94:13 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:94:37 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:99:13 + | +LL | pub mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:100:13 + | +LL | pub mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` 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: aborting due to 40 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs new file mode 100644 index 0000000000000..5a996b0c1d469 --- /dev/null +++ b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs @@ -0,0 +1,25 @@ +#![feature(mut_restriction, unsafe_fields)] + +pub mod foo { + pub struct Foo { + pub mut(crate::foo) x: i32, //~ ERROR incorrect `mut` restriction + pub mut(crate::foo) unsafe y: i32, //~ ERROR incorrect `mut` restriction + } + + pub struct TupFoo(pub mut(crate::foo) i32); //~ ERROR incorrect `mut` restriction + + pub enum EnumFoo { + Var { + mut(crate::foo) x: i32, //~ ERROR incorrect `mut` restriction + mut(crate::foo) unsafe y: i32 //~ ERROR incorrect `mut` restriction + }, + Tup(mut(crate::foo) i32), //~ ERROR incorrect `mut` restriction + } + + pub union UnionFoo { + pub mut(crate::foo) x: i32, //~ ERROR incorrect `mut` restriction + pub mut(crate::foo) unsafe y: i32, //~ ERROR incorrect `mut` restriction + } +} + +fn main() {} diff --git a/tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr new file mode 100644 index 0000000000000..50e5216a16283 --- /dev/null +++ b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr @@ -0,0 +1,130 @@ +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:5:17 + | +LL | pub mut(crate::foo) x: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) x: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:6:17 + | +LL | pub mut(crate::foo) unsafe y: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:9:31 + | +LL | pub struct TupFoo(pub mut(crate::foo) i32); + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub struct TupFoo(pub mut(in crate::foo) i32); + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:13:17 + | +LL | mut(crate::foo) x: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | mut(in crate::foo) x: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:14:17 + | +LL | mut(crate::foo) unsafe y: i32 + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | mut(in crate::foo) unsafe y: i32 + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:16:17 + | +LL | Tup(mut(crate::foo) i32), + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | Tup(mut(in crate::foo) i32), + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:20:17 + | +LL | pub mut(crate::foo) x: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) x: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:21:17 + | +LL | pub mut(crate::foo) unsafe y: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ++ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/on-unimplemented/format_specs.rs b/tests/ui/on-unimplemented/format_specs.rs new file mode 100644 index 0000000000000..f92674ab4a888 --- /dev/null +++ b/tests/ui/on-unimplemented/format_specs.rs @@ -0,0 +1,13 @@ +#![feature(rustc_attrs)] + +#[rustc_on_unimplemented( + message = "normal: {This}, path: {This:path}, resolved: {This:resolved}" +)] +pub trait Trait<'lifetime, const CONST_GENERIC: usize, A, B> where A: Send {} + +fn take_trait<'a, T: Trait<'a, 6, u8, U>, U>(_: T) {} + +fn main() { + take_trait(()); + //~^ERROR normal: Trait, path: Trait<'lifetime, CONST_GENERIC, A, B>, resolved: Trait<'_, 6, u8, _> +} diff --git a/tests/ui/on-unimplemented/format_specs.stderr b/tests/ui/on-unimplemented/format_specs.stderr new file mode 100644 index 0000000000000..3163323f34026 --- /dev/null +++ b/tests/ui/on-unimplemented/format_specs.stderr @@ -0,0 +1,22 @@ +error[E0277]: normal: Trait, path: Trait<'lifetime, CONST_GENERIC, A, B>, resolved: Trait<'_, 6, u8, _> + --> $DIR/format_specs.rs:11:16 + | +LL | take_trait(()); + | ---------- ^^ the trait `Trait<'_, 6, u8, _>` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/format_specs.rs:6:1 + | +LL | pub trait Trait<'lifetime, const CONST_GENERIC: usize, A, B> where A: Send {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `take_trait` + --> $DIR/format_specs.rs:8:22 + | +LL | fn take_trait<'a, T: Trait<'a, 6, u8, U>, U>(_: T) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `take_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs index 20a4b85c01e8d..06debee1ff9e8 100644 --- a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs +++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs @@ -1,7 +1,6 @@ //@ aux-crate:priv:shared=shared.rs -//@ aux-crate:reexport=reexport.rs +//@ aux-crate:priv:reexport=reexport.rs //@ compile-flags: -Zunstable-options -//@ check-pass // A shared dependency, where a private dependency reexports a public dependency. // @@ -21,12 +20,12 @@ extern crate shared; extern crate reexport; -// FIXME: This should trigger. pub fn leaks_priv() -> shared::Shared { + //~^ ERROR type `Shared` from private dependency 'shared' in public interface shared::Shared } -// FIXME: This should trigger. pub fn leaks_priv_reexport() -> reexport::Shared { + //~^ ERROR type `Shared` from private dependency 'shared' in public interface reexport::Shared } diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.stderr b/tests/ui/privacy/pub-priv-dep/shared_both_private.stderr new file mode 100644 index 0000000000000..b26e026b2d67b --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.stderr @@ -0,0 +1,20 @@ +error: type `Shared` from private dependency 'shared' in public interface + --> $DIR/shared_both_private.rs:23:1 + | +LL | pub fn leaks_priv() -> shared::Shared { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/shared_both_private.rs:18:9 + | +LL | #![deny(exported_private_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `Shared` from private dependency 'shared' in public interface + --> $DIR/shared_both_private.rs:28:1 + | +LL | pub fn leaks_priv_reexport() -> reexport::Shared { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/privacy/reach-type-alias-issue-156778.rs b/tests/ui/privacy/reach-type-alias-issue-156778.rs new file mode 100644 index 0000000000000..4297a44038cfc --- /dev/null +++ b/tests/ui/privacy/reach-type-alias-issue-156778.rs @@ -0,0 +1,20 @@ +//@ check-pass +#![feature(lazy_type_alias)] + +use src::hidden_core; +mod src { + mod aliases { + use hidden_core::InternalStruct; + pub type ExposedType = InternalStruct; + } + pub mod hidden_core { + use super::aliases::ExposedType; + pub struct InternalStruct { + _x: T, + } + pub fn new() -> ExposedType { + InternalStruct { _x: 1.0 } + } + } +} +fn main() {} diff --git a/tests/ui/range/range_traits-1.rs b/tests/ui/range/range_traits-1.rs index e28e47435c2c2..63e22b7420368 100644 --- a/tests/ui/range/range_traits-1.rs +++ b/tests/ui/range/range_traits-1.rs @@ -3,23 +3,17 @@ use std::ops::*; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] struct AllTheRanges { a: Range, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord b: RangeTo, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord c: RangeFrom, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord d: RangeFull, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord e: RangeInclusive, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord f: RangeToInclusive, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord } fn main() {} diff --git a/tests/ui/range/range_traits-1.stderr b/tests/ui/range/range_traits-1.stderr index ab1035778cd6c..3a200adfc96f8 100644 --- a/tests/ui/range/range_traits-1.stderr +++ b/tests/ui/range/range_traits-1.stderr @@ -1,69 +1,3 @@ -error[E0277]: can't compare `std::ops::Range` with `std::ops::Range` - --> $DIR/range_traits-1.rs:5:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -LL | struct AllTheRanges { -LL | a: Range, - | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::Range` - -error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo` - --> $DIR/range_traits-1.rs:8:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | b: RangeTo, - | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` - -error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFrom` - --> $DIR/range_traits-1.rs:11:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | c: RangeFrom, - | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` - -error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` - --> $DIR/range_traits-1.rs:14:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | d: RangeFull, - | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` - -error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::RangeInclusive` - --> $DIR/range_traits-1.rs:17:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | e: RangeInclusive, - | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` - -error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops::RangeToInclusive` - --> $DIR/range_traits-1.rs:20:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | f: RangeToInclusive, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` - error[E0277]: the trait bound `std::ops::Range: Ord` is not satisfied --> $DIR/range_traits-1.rs:5:5 | @@ -74,7 +8,7 @@ LL | a: Range, | ^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::Range` error[E0277]: the trait bound `std::ops::RangeTo: Ord` is not satisfied - --> $DIR/range_traits-1.rs:8:5 + --> $DIR/range_traits-1.rs:7:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -83,7 +17,7 @@ LL | b: RangeTo, | ^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeTo` error[E0277]: the trait bound `std::ops::RangeFrom: Ord` is not satisfied - --> $DIR/range_traits-1.rs:11:5 + --> $DIR/range_traits-1.rs:9:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -92,7 +26,7 @@ LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFrom` error[E0277]: the trait bound `std::ops::RangeFull: Ord` is not satisfied - --> $DIR/range_traits-1.rs:14:5 + --> $DIR/range_traits-1.rs:11:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -101,7 +35,7 @@ LL | d: RangeFull, | ^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFull` error[E0277]: the trait bound `std::ops::RangeInclusive: Ord` is not satisfied - --> $DIR/range_traits-1.rs:17:5 + --> $DIR/range_traits-1.rs:13:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -110,7 +44,7 @@ LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeInclusive` error[E0277]: the trait bound `std::ops::RangeToInclusive: Ord` is not satisfied - --> $DIR/range_traits-1.rs:20:5 + --> $DIR/range_traits-1.rs:15:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -118,6 +52,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeToInclusive` -error: aborting due to 12 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs b/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs new file mode 100644 index 0000000000000..465be1b52fb2c --- /dev/null +++ b/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs @@ -0,0 +1,40 @@ +//@ check-pass +// Regression test for rust-lang/rust#156482. +// This used to ICE in MIR building when a THIR `ExprKind::Reborrow` +// was categorized as a place but `expr_as_place` treated it as unreachable. + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct Thing<'a>(&'a ()); + +impl<'a> Reborrow for Thing<'a> {} + +fn foo(x: Thing<'_>) { + let _: Thing<'_> = x; +} + +#[allow(unused)] +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +#[allow(unused)] +struct CustomRef<'a, T>(&'a T); +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +fn main() { + let a = CustomMut(&mut ()); + + let _: CustomMut<'_, ()> = a; + let _: CustomMut<'_, ()> = a; + + let _: CustomRef<'_, ()> = a; + let _: CustomRef<'_, ()> = a; +} diff --git a/tests/ui/reborrow/reborrow_multi_field_validation.rs b/tests/ui/reborrow/reborrow_multi_field_validation.rs new file mode 100644 index 0000000000000..9be4dd89f16c0 --- /dev/null +++ b/tests/ui/reborrow/reborrow_multi_field_validation.rs @@ -0,0 +1,15 @@ +#![feature(reborrow)] + +use std::marker::Reborrow; + +// Regression test: `reborrow_info` must validate ALL data fields, +// not just stop at the first Reborrow field. + +struct Bad<'a> { + first: &'a mut i32, + second: String, //~ ERROR the trait bound `String: Copy` is not satisfied +} + +impl<'a> Reborrow for Bad<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/reborrow_multi_field_validation.stderr b/tests/ui/reborrow/reborrow_multi_field_validation.stderr new file mode 100644 index 0000000000000..26d52818beeff --- /dev/null +++ b/tests/ui/reborrow/reborrow_multi_field_validation.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/reborrow_multi_field_validation.rs:10:5 + | +LL | second: String, + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-17121.rs b/tests/ui/regions/relate-bound-region-in-method-return.rs similarity index 87% rename from tests/ui/issues/issue-17121.rs rename to tests/ui/regions/relate-bound-region-in-method-return.rs index 6bb89a4aa7b43..a994f7f1af0cb 100644 --- a/tests/ui/issues/issue-17121.rs +++ b/tests/ui/regions/relate-bound-region-in-method-return.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ check-pass #![allow(dead_code)] diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index 7ce5ebf81e310..f8ef315b9cc78 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -1,21 +1,13 @@ error[E0382]: use of moved value: `a` - --> $DIR/dbg-macro-move-semantics.rs:9:18 + --> $DIR/dbg-macro-move-semantics.rs:9:13 | LL | let a = NoCopy(0); | - move occurs because `a` has type `NoCopy`, which does not implement the `Copy` trait LL | let _ = dbg!(a); - | - value moved here + | ------- value moved here LL | let _ = dbg!(a); - | ^ value used here after move + | ^^^^^^^ value used here after move | -note: if `NoCopy` implemented `Clone`, you could clone the value - --> $DIR/dbg-macro-move-semantics.rs:4:1 - | -LL | struct NoCopy(usize); - | ^^^^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let _ = dbg!(a); - | - you could clone this value help: consider borrowing instead of transferring ownership | LL | let _ = dbg!(&a); diff --git a/tests/ui/share-trait/share-trait-arc.rs b/tests/ui/share-trait/share-trait-arc.rs new file mode 100644 index 0000000000000..1a4f421caeeb9 --- /dev/null +++ b/tests/ui/share-trait/share-trait-arc.rs @@ -0,0 +1,31 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::clone::Share; +use std::sync::{Arc, Mutex}; + +trait Value { + fn get(&self) -> i32; +} + +impl Value for Mutex { + fn get(&self) -> i32 { + *self.lock().unwrap() + } +} + +fn main() { + let value = Arc::new(Mutex::new(1)); + let shared = value.share(); + + assert!(Arc::ptr_eq(&value, &shared)); + *shared.lock().unwrap() = 2; + assert_eq!(*value.lock().unwrap(), 2); + + let dyn_value: Arc = Arc::new(Mutex::new(3)); + let shared_dyn_value = dyn_value.share(); + + assert!(Arc::ptr_eq(&dyn_value, &shared_dyn_value)); + assert_eq!(shared_dyn_value.get(), 3); +} diff --git a/tests/ui/share-trait/share-trait-mpsc-sender.rs b/tests/ui/share-trait/share-trait-mpsc-sender.rs new file mode 100644 index 0000000000000..1939b10fbf43a --- /dev/null +++ b/tests/ui/share-trait/share-trait-mpsc-sender.rs @@ -0,0 +1,19 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::clone::Share; +use std::sync::mpsc::channel; + +fn main() { + let (sender, receiver) = channel(); + let shared_sender = sender.share(); + + sender.send(1).unwrap(); + shared_sender.send(2).unwrap(); + + let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()]; + received.sort(); + + assert_eq!(received, [1, 2]); +} diff --git a/tests/ui/share-trait/share-trait-mpsc-sync-sender.rs b/tests/ui/share-trait/share-trait-mpsc-sync-sender.rs new file mode 100644 index 0000000000000..6b10d05324283 --- /dev/null +++ b/tests/ui/share-trait/share-trait-mpsc-sync-sender.rs @@ -0,0 +1,19 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::clone::Share; +use std::sync::mpsc::sync_channel; + +fn main() { + let (sender, receiver) = sync_channel(2); + let shared_sender = sender.share(); + + sender.send(1).unwrap(); + shared_sender.send(2).unwrap(); + + let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()]; + received.sort(); + + assert_eq!(received, [1, 2]); +} diff --git a/tests/ui/share-trait/share-trait-no-feature.rs b/tests/ui/share-trait/share-trait-no-feature.rs new file mode 100644 index 0000000000000..2b4791d2eb034 --- /dev/null +++ b/tests/ui/share-trait/share-trait-no-feature.rs @@ -0,0 +1,20 @@ +use std::clone::Share; +//~^ ERROR use of unstable library feature `share_trait` + +#[derive(Clone)] +struct Alias; + +impl Share for Alias {} +//~^ ERROR use of unstable library feature `share_trait` + +fn share_generic(value: &T) -> T { + //~^ ERROR use of unstable library feature `share_trait` + value.share() + //~^ ERROR use of unstable library feature `share_trait` +} + +fn main() { + let value = Alias; + let _ = Share::share(&value); + //~^ ERROR use of unstable library feature `share_trait` +} diff --git a/tests/ui/share-trait/share-trait-no-feature.stderr b/tests/ui/share-trait/share-trait-no-feature.stderr new file mode 100644 index 0000000000000..344f5d8670f63 --- /dev/null +++ b/tests/ui/share-trait/share-trait-no-feature.stderr @@ -0,0 +1,53 @@ +error[E0658]: use of unstable library feature `share_trait` + --> $DIR/share-trait-no-feature.rs:1:5 + | +LL | use std::clone::Share; + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` 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 `share_trait` + --> $DIR/share-trait-no-feature.rs:7:6 + | +LL | impl Share for Alias {} + | ^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` 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 `share_trait` + --> $DIR/share-trait-no-feature.rs:10:21 + | +LL | fn share_generic(value: &T) -> T { + | ^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` 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 `share_trait` + --> $DIR/share-trait-no-feature.rs:18:13 + | +LL | let _ = Share::share(&value); + | ^^^^^^^^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` 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 `share_trait` + --> $DIR/share-trait-no-feature.rs:12:11 + | +LL | value.share() + | ^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` 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: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/share-trait/share-trait-non-implementors.rs b/tests/ui/share-trait/share-trait-non-implementors.rs new file mode 100644 index 0000000000000..a53f10fb9e6b3 --- /dev/null +++ b/tests/ui/share-trait/share-trait-non-implementors.rs @@ -0,0 +1,19 @@ +#![feature(share_trait)] + +use std::clone::Share; + +fn require_share() {} + +fn main() { + require_share::<&mut i32>(); + //~^ ERROR the trait bound `&mut i32: Share` is not satisfied + + require_share::(); + //~^ ERROR the trait bound `String: Share` is not satisfied + + require_share::>(); + //~^ ERROR the trait bound `Vec: Share` is not satisfied + + require_share::>(); + //~^ ERROR the trait bound `Box: Share` is not satisfied +} diff --git a/tests/ui/share-trait/share-trait-non-implementors.stderr b/tests/ui/share-trait/share-trait-non-implementors.stderr new file mode 100644 index 0000000000000..8056e34beea55 --- /dev/null +++ b/tests/ui/share-trait/share-trait-non-implementors.stderr @@ -0,0 +1,76 @@ +error[E0277]: the trait bound `&mut i32: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:8:21 + | +LL | require_share::<&mut i32>(); + | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `&mut i32` + | + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender + = note: `Share` is implemented for `&i32`, but not for `&mut i32` +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error[E0277]: the trait bound `String: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:11:21 + | +LL | require_share::(); + | ^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `String` + | + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error[E0277]: the trait bound `Vec: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:14:21 + | +LL | require_share::>(); + | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `Vec` + | + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error[E0277]: the trait bound `Box: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:17:21 + | +LL | require_share::>(); + | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `Box` + | + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/share-trait/share-trait-not-in-prelude.rs b/tests/ui/share-trait/share-trait-not-in-prelude.rs new file mode 100644 index 0000000000000..4876fac2b8c1d --- /dev/null +++ b/tests/ui/share-trait/share-trait-not-in-prelude.rs @@ -0,0 +1,9 @@ +#![feature(share_trait)] + +#[derive(Clone)] +struct Alias; + +impl Share for Alias {} +//~^ ERROR cannot find trait `Share` in this scope + +fn main() {} diff --git a/tests/ui/share-trait/share-trait-not-in-prelude.stderr b/tests/ui/share-trait/share-trait-not-in-prelude.stderr new file mode 100644 index 0000000000000..733c8f2d2b215 --- /dev/null +++ b/tests/ui/share-trait/share-trait-not-in-prelude.stderr @@ -0,0 +1,14 @@ +error[E0405]: cannot find trait `Share` in this scope + --> $DIR/share-trait-not-in-prelude.rs:6:6 + | +LL | impl Share for Alias {} + | ^^^^^ not found in this scope + | +help: consider importing this trait + | +LL + use std::clone::Share; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0405`. diff --git a/tests/ui/share-trait/share-trait-rc.rs b/tests/ui/share-trait/share-trait-rc.rs new file mode 100644 index 0000000000000..7de6936bcc9cb --- /dev/null +++ b/tests/ui/share-trait/share-trait-rc.rs @@ -0,0 +1,32 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::cell::Cell; +use std::clone::Share; +use std::rc::Rc; + +trait Value { + fn get(&self) -> i32; +} + +impl Value for Cell { + fn get(&self) -> i32 { + Cell::get(self) + } +} + +fn main() { + let value = Rc::new(Cell::new(1)); + let shared = value.share(); + + assert!(Rc::ptr_eq(&value, &shared)); + shared.set(2); + assert_eq!(value.get(), 2); + + let dyn_value: Rc = Rc::new(Cell::new(3)); + let shared_dyn_value = dyn_value.share(); + + assert!(Rc::ptr_eq(&dyn_value, &shared_dyn_value)); + assert_eq!(shared_dyn_value.get(), 3); +} diff --git a/tests/ui/share-trait/share-trait-requires-clone.rs b/tests/ui/share-trait/share-trait-requires-clone.rs new file mode 100644 index 0000000000000..a70ae3ade37d0 --- /dev/null +++ b/tests/ui/share-trait/share-trait-requires-clone.rs @@ -0,0 +1,10 @@ +#![feature(share_trait)] + +use std::clone::Share; + +struct NotClone; + +impl Share for NotClone {} +//~^ ERROR the trait bound `NotClone: Clone` is not satisfied + +fn main() {} diff --git a/tests/ui/share-trait/share-trait-requires-clone.stderr b/tests/ui/share-trait/share-trait-requires-clone.stderr new file mode 100644 index 0000000000000..ac775db690a40 --- /dev/null +++ b/tests/ui/share-trait/share-trait-requires-clone.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `NotClone: Clone` is not satisfied + --> $DIR/share-trait-requires-clone.rs:7:16 + | +LL | impl Share for NotClone {} + | ^^^^^^^^ the trait `Clone` is not implemented for `NotClone` + | +note: required by a bound in `Share` + --> $SRC_DIR/core/src/clone.rs:LL:COL +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/share-trait/share-trait.rs b/tests/ui/share-trait/share-trait.rs new file mode 100644 index 0000000000000..7a099cd96cb85 --- /dev/null +++ b/tests/ui/share-trait/share-trait.rs @@ -0,0 +1,44 @@ +//@ check-pass + +#![feature(share_trait)] + +extern crate core; + +use core::clone::Share; + +#[derive(Debug, PartialEq)] +struct Alias(u8); + +impl Clone for Alias { + fn clone(&self) -> Self { + Alias(self.0 + 1) + } +} + +impl Share for Alias {} + +fn share_generic(value: &T) -> T { + value.share() +} + +fn main() { + let value = Alias(1); + + assert_eq!(Share::share(&value), Alias(2)); + assert_eq!(std::clone::Share::share(&value), Alias(2)); + assert_eq!(value.share(), Alias(2)); + assert_eq!(share_generic(&value), Alias(2)); + + let number = 3; + let shared = &number; + let shared_again = shared.share(); + let shared_fqs: &i32 = Share::share(&shared); + let shared_generic: &i32 = share_generic(&shared); + + assert!(std::ptr::eq(shared, shared_again)); + assert!(std::ptr::eq(shared_fqs, shared)); + assert!(std::ptr::eq(shared_generic, shared)); + + let slice: &[i32] = &[1, 2, 3]; + assert!(std::ptr::eq(slice, slice.share())); +} diff --git a/tests/ui/single-use-lifetime/issue-104440.rs b/tests/ui/single-use-lifetime/issue-104440.rs index cecd17fb930bf..6c4398f4372df 100644 --- a/tests/ui/single-use-lifetime/issue-104440.rs +++ b/tests/ui/single-use-lifetime/issue-104440.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(decl_macro, rustc_attrs)] #![deny(single_use_lifetimes)] @@ -35,7 +37,7 @@ mod type_params { mod lifetime_params { macro m($a:lifetime) { - fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once + fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { (t1, t2) } } @@ -60,7 +62,7 @@ mod lifetime_params { } } - m!('a); //~ ERROR lifetime parameter `'a` only used once + m!('a); n!('a); p!('a); } diff --git a/tests/ui/single-use-lifetime/issue-104440.stderr b/tests/ui/single-use-lifetime/issue-104440.stderr deleted file mode 100644 index 54ded31dcbe89..0000000000000 --- a/tests/ui/single-use-lifetime/issue-104440.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: lifetime parameter `'a` only used once - --> $DIR/issue-104440.rs:63:8 - | -LL | m!('a); - | ^^ - | | - | this lifetime... - | ...is used only here - | -note: the lint level is defined here - --> $DIR/issue-104440.rs:2:9 - | -LL | #![deny(single_use_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: lifetime parameter `'a` only used once - --> $DIR/issue-104440.rs:38:30 - | -LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { - | ^^ this lifetime... -- ...is used only here -... -LL | m!('a); - | ------ in this macro invocation - | - = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors - diff --git a/tests/ui/single-use-lifetime/issue-117965.rs b/tests/ui/single-use-lifetime/issue-117965.rs index 5eb2a03e13da4..7c3b32651736c 100644 --- a/tests/ui/single-use-lifetime/issue-117965.rs +++ b/tests/ui/single-use-lifetime/issue-117965.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![deny(single_use_lifetimes)] pub enum Data<'a> { @@ -7,7 +9,6 @@ pub enum Data<'a> { impl<'a> Data<'a> { pub fn get<'b: 'a>(&'b self) -> &'a str { - //~^ ERROR lifetime parameter `'b` only used once match &self { Self::Borrowed(val) => val, Self::Owned(val) => &val, diff --git a/tests/ui/single-use-lifetime/issue-117965.stderr b/tests/ui/single-use-lifetime/issue-117965.stderr deleted file mode 100644 index ed14ab92f4d16..0000000000000 --- a/tests/ui/single-use-lifetime/issue-117965.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: lifetime parameter `'b` only used once - --> $DIR/issue-117965.rs:9:16 - | -LL | pub fn get<'b: 'a>(&'b self) -> &'a str { - | ^^ -- ...is used only here - | | - | this lifetime... - | -note: the lint level is defined here - --> $DIR/issue-117965.rs:1:9 - | -LL | #![deny(single_use_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/single-use-lifetime/lifetime-bounds.rs b/tests/ui/single-use-lifetime/lifetime-bounds.rs new file mode 100644 index 0000000000000..a29d68a0b4a9b --- /dev/null +++ b/tests/ui/single-use-lifetime/lifetime-bounds.rs @@ -0,0 +1,34 @@ +// Regression test for https://github.com/rust-lang/rust/issues/153836. +#![deny(single_use_lifetimes)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'a>(&'a i32); +struct Bar<'b>(&'b i32); + +fn function<'a, 'b: 'a>(_: Foo<'a>, _: Bar<'b>) {} + +type FnPtr = for<'a, 'b: 'a> fn(Foo<'a>, Bar<'b>); +//~^ ERROR bounds cannot be used in this context + +trait WhereBound<'a, 'b> {} + +fn where_bound() +where + T: for<'a, 'b: 'a> WhereBound<'a, 'b>, + //~^ ERROR bounds cannot be used in this context +{ +} + +trait ImplTrait<'a> { + fn foo(self, foo: Foo<'a>); +} + +impl<'a, 'b: 'a> ImplTrait<'a> for Bar<'b> { + fn foo(self, foo: Foo<'a>) { + let _: &'a i32 = self.0; + let _: Foo<'a> = foo; + } +} + +fn main() {} diff --git a/tests/ui/single-use-lifetime/lifetime-bounds.stderr b/tests/ui/single-use-lifetime/lifetime-bounds.stderr new file mode 100644 index 0000000000000..3cc9e8ac90a65 --- /dev/null +++ b/tests/ui/single-use-lifetime/lifetime-bounds.stderr @@ -0,0 +1,14 @@ +error: bounds cannot be used in this context + --> $DIR/lifetime-bounds.rs:11:26 + | +LL | type FnPtr = for<'a, 'b: 'a> fn(Foo<'a>, Bar<'b>); + | ^^ + +error: bounds cannot be used in this context + --> $DIR/lifetime-bounds.rs:18:20 + | +LL | T: for<'a, 'b: 'a> WhereBound<'a, 'b>, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index a91a15bc63dff..b4d8277a46c00 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -33,8 +33,8 @@ ast-stats - Trait 352 (NN.N%) 4 ast-stats AssocItem 320 (NN.N%) 4 80 ast-stats - Fn 160 (NN.N%) 2 ast-stats - Type 160 (NN.N%) 2 +ast-stats FieldDef 272 (NN.N%) 2 136 ast-stats Variant 208 (NN.N%) 2 104 -ast-stats FieldDef 208 (NN.N%) 2 104 ast-stats Block 192 (NN.N%) 6 32 ast-stats Stmt 160 (NN.N%) 5 32 ast-stats - Let 32 (NN.N%) 1 @@ -57,7 +57,7 @@ ast-stats GenericArgs 40 (NN.N%) 1 40 ast-stats - AngleBracketed 40 (NN.N%) 1 ast-stats Crate 40 (NN.N%) 1 40 ast-stats ---------------------------------------------------------------- -ast-stats Total 7_560 127 +ast-stats Total 7_624 127 ast-stats ================================================================ hir-stats ================================================================ hir-stats HIR STATS: input_stats diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index e46e7e02862fa..c70895067eb5f 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -3,7 +3,6 @@ macro-stats MACRO EXPANSION STATS: macro_stats macro-stats Macro Name Uses Lines Avg Lines Bytes Avg Bytes macro-stats ----------------------------------------------------------------------------------- macro-stats #[derive(Clone)] 8 67 8.4 1_879 234.9 -macro-stats #[derive(PartialOrd)] 1 17 17.0 675 675.0 macro-stats #[derive(Hash)] 2 17 8.5 565 282.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 @@ -11,6 +10,7 @@ macro-stats #[derive(Default)] 2 16 8.0 macro-stats #[derive(Eq)] 1 11 11.0 312 312.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 +macro-stats #[derive(PartialOrd)] 1 8 8.0 254 254.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0 macro-stats p! 1 3 3.0 32 32.0 macro-stats trait_impl_tys! 1 2 2.0 28 28.0 diff --git a/tests/ui/issues/issue-17800.rs b/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs similarity index 76% rename from tests/ui/issues/issue-17800.rs rename to tests/ui/structs-enums/tuple-variant-as-struct-variant.rs index 5254f45d7c2de..53da4622013ba 100644 --- a/tests/ui/issues/issue-17800.rs +++ b/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs @@ -1,3 +1,5 @@ +//! Regression test for . + enum MyOption { MySome(T), MyNone, diff --git a/tests/ui/issues/issue-17800.stderr b/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr similarity index 89% rename from tests/ui/issues/issue-17800.stderr rename to tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr index 322c77eaa1dc5..1170e3b7bfd8a 100644 --- a/tests/ui/issues/issue-17800.stderr +++ b/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0769]: tuple variant `MyOption::MySome` written as struct variant - --> $DIR/issue-17800.rs:8:9 + --> $DIR/tuple-variant-as-struct-variant.rs:10:9 | LL | MyOption::MySome { x: 42 } => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/target-feature/inline-always.aarch64.stderr b/tests/ui/target-feature/inline-always.aarch64.stderr deleted file mode 100644 index 8b58923f2170f..0000000000000 --- a/tests/ui/target-feature/inline-always.aarch64.stderr +++ /dev/null @@ -1,60 +0,0 @@ -warning: call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined - --> $DIR/inline-always.rs:20:5 - | -LL | target_feature_identity(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `target_feature_identity` but missing from `call_no_target_features`: neon, fp16 -note: `target_feature_identity` is defined here - --> $DIR/inline-always.rs:17:1 - | -LL | pub unsafe fn target_feature_identity() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[warn(inline_always_mismatching_target_features)]` on by default -help: add `#[target_feature]` attribute to `call_no_target_features` - | -LL + #[target_feature(enable = "neon,fp16")] -LL | unsafe fn call_no_target_features() { - | - -warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined - --> $DIR/inline-always.rs:23:5 - | -LL | multiple_target_features(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `multiple_target_features` but missing from `call_no_target_features`: fp16, sve, rdm -note: `multiple_target_features` is defined here - --> $DIR/inline-always.rs:53:1 - | -LL | fn multiple_target_features() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[target_feature]` attribute to `call_no_target_features` - | -LL + #[target_feature(enable = "fp16,sve,rdm")] -LL | unsafe fn call_no_target_features() { - | - -warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined - --> $DIR/inline-always.rs:29:5 - | -LL | multiple_target_features(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `multiple_target_features` but missing from `call_to_first_set`: rdm -note: `multiple_target_features` is defined here - --> $DIR/inline-always.rs:53:1 - | -LL | fn multiple_target_features() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[target_feature]` attribute to `call_to_first_set` - | -LL + #[target_feature(enable = "rdm")] -LL | unsafe fn call_to_first_set() { - | - -warning: 3 warnings emitted - diff --git a/tests/ui/target-feature/inline-always.rs b/tests/ui/target-feature/inline-always.rs deleted file mode 100644 index c1334bb6016b2..0000000000000 --- a/tests/ui/target-feature/inline-always.rs +++ /dev/null @@ -1,55 +0,0 @@ -//@ add-minicore -//@ build-pass -//@ compile-flags: --crate-type=lib -//@ revisions: aarch64 -//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] needs-llvm-components: aarch64 -//@ ignore-backends: gcc - -#![feature(no_core, target_feature_inline_always)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[inline(always)] -#[target_feature(enable = "neon,fp16")] -pub unsafe fn target_feature_identity() {} - -unsafe fn call_no_target_features() { - target_feature_identity(); - //~^ WARNING call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined [inline_always_mismatching_target_features] - global_feature_enabled(); - multiple_target_features(); - //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] -} - -#[target_feature(enable = "fp16,sve")] -unsafe fn call_to_first_set() { - multiple_target_features(); - //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] -} - -/* You can't have "fhm" without "fp16" */ -#[target_feature(enable = "fhm")] -unsafe fn mismatching_features() { - target_feature_identity() -} - -#[target_feature(enable = "fp16")] -unsafe fn matching_target_features() { - target_feature_identity() -} - -#[inline(always)] -#[target_feature(enable = "neon")] -unsafe fn global_feature_enabled() { - -} - -#[inline(always)] -#[target_feature(enable = "fp16,sve")] -#[target_feature(enable="rdm")] -fn multiple_target_features() { - - } diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 0c380d69b1433..968fbdc1dc7f1 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -67,8 +67,7 @@ trait Baz {} #[inline(always)] //~^ ERROR: cannot use `#[inline(always)]` -//~| NOTE: see issue #145574 for more information -//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//~| NOTE: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 #[target_feature(enable = "sse2")] unsafe fn test() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 6de12edb037fe..cb19bdc60ceb4 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -121,7 +121,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on statics - --> $DIR/invalid-attribute.rs:75:1 + --> $DIR/invalid-attribute.rs:74:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on trait impl blocks - --> $DIR/invalid-attribute.rs:79:1 + --> $DIR/invalid-attribute.rs:78:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on inherent impl blocks - --> $DIR/invalid-attribute.rs:85:1 + --> $DIR/invalid-attribute.rs:84:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on expressions - --> $DIR/invalid-attribute.rs:106:5 + --> $DIR/invalid-attribute.rs:105:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,22 +153,20 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on closures - --> $DIR/invalid-attribute.rs:112:5 + --> $DIR/invalid-attribute.rs:111:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: `#[target_feature]` can be applied to functions and methods -error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` +error: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:68:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ | - = note: see issue #145574 for more information - = help: add `#![feature(target_feature_inline_always)]` 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: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 error: the feature named `foo` is not valid for this target --> $DIR/invalid-attribute.rs:26:18 @@ -179,7 +177,7 @@ LL | #[target_feature(enable = "foo")] = help: valid names are: `fma`, `xop`, `adx`, `aes`, and `avx` and 75 more error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/invalid-attribute.rs:81:1 + --> $DIR/invalid-attribute.rs:80:1 | LL | impl Quux for u8 {} | ^^^^^^^^^^^^^^^^ missing `foo` in implementation @@ -188,7 +186,7 @@ LL | fn foo(); | --------- `foo` from trait error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/invalid-attribute.rs:95:5 + --> $DIR/invalid-attribute.rs:94:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -197,13 +195,13 @@ LL | fn foo() {} | -------- not an `unsafe` function error[E0053]: method `foo` has an incompatible type for trait - --> $DIR/invalid-attribute.rs:98:5 + --> $DIR/invalid-attribute.rs:97:5 | LL | fn foo() {} | ^^^^^^^^ expected safe fn, found unsafe fn | note: type in trait - --> $DIR/invalid-attribute.rs:90:5 + --> $DIR/invalid-attribute.rs:89:5 | LL | fn foo(); | ^^^^^^^^^ @@ -211,7 +209,7 @@ LL | fn foo(); found signature `#[target_features] fn()` error: the feature named `+sse2` is not valid for this target - --> $DIR/invalid-attribute.rs:117:18 + --> $DIR/invalid-attribute.rs:116:18 | LL | #[target_feature(enable = "+sse2")] | ^^^^^^^^^^^^^^^^ `+sse2` is not valid for this target @@ -223,7 +221,7 @@ LL + #[target_feature(enable = "sse2")] | error: the feature named `sse5` is not valid for this target - --> $DIR/invalid-attribute.rs:122:18 + --> $DIR/invalid-attribute.rs:121:18 | LL | #[target_feature(enable = "sse5")] | ^^^^^^^^^^^^^^^ `sse5` is not valid for this target @@ -231,7 +229,7 @@ LL | #[target_feature(enable = "sse5")] = help: valid names are: `sse`, `sse2`, `sse3`, `sse4a`, and `ssse3` and 75 more error: the feature named `avx512` is not valid for this target - --> $DIR/invalid-attribute.rs:127:18 + --> $DIR/invalid-attribute.rs:126:18 | LL | #[target_feature(enable = "avx512")] | ^^^^^^^^^^^^^^^^^ `avx512` is not valid for this target @@ -240,5 +238,5 @@ LL | #[target_feature(enable = "avx512")] error: aborting due to 26 previous errors -Some errors have detailed explanations: E0046, E0053, E0539, E0658. +Some errors have detailed explanations: E0046, E0053, E0539. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/issues/issue-17954.rs b/tests/ui/thread-local/thread-local-borrow-past-function-end.rs similarity index 83% rename from tests/ui/issues/issue-17954.rs rename to tests/ui/thread-local/thread-local-borrow-past-function-end.rs index eb6a3d70f58e2..12938f0a0a2d4 100644 --- a/tests/ui/issues/issue-17954.rs +++ b/tests/ui/thread-local/thread-local-borrow-past-function-end.rs @@ -1,3 +1,4 @@ +//! Regression test for . #![feature(thread_local)] #[thread_local] diff --git a/tests/ui/issues/issue-17954.stderr b/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr similarity index 86% rename from tests/ui/issues/issue-17954.stderr rename to tests/ui/thread-local/thread-local-borrow-past-function-end.stderr index 0dddea8336483..7686a6b714764 100644 --- a/tests/ui/issues/issue-17954.stderr +++ b/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr @@ -1,5 +1,5 @@ error[E0712]: thread-local variable borrowed past end of function - --> $DIR/issue-17954.rs:7:13 + --> $DIR/thread-local-borrow-past-function-end.rs:8:13 | LL | let a = &FOO; | ^^^^ thread-local variables cannot be borrowed beyond the end of the function diff --git a/tests/ui/type/pattern_types/derives_fail.rs b/tests/ui/type/pattern_types/derives_fail.rs index a3fbad6672071..a7d6a49c4b2d0 100644 --- a/tests/ui/type/pattern_types/derives_fail.rs +++ b/tests/ui/type/pattern_types/derives_fail.rs @@ -14,7 +14,6 @@ struct Nanoseconds(NanoI32); //~| ERROR: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied //~| ERROR: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied //~| ERROR: the trait bound `(i32) is 0..=999999999: Default` is not satisfied -//~| ERROR: can't compare `(i32) is 0..=999999999` with `_` //~| ERROR: `==` cannot be applied type NanoI32 = crate::pattern_type!(i32 is 0..=999_999_999); diff --git a/tests/ui/type/pattern_types/derives_fail.stderr b/tests/ui/type/pattern_types/derives_fail.stderr index 45c9bae1f2806..b79b34adda52f 100644 --- a/tests/ui/type/pattern_types/derives_fail.stderr +++ b/tests/ui/type/pattern_types/derives_fail.stderr @@ -37,17 +37,6 @@ LL | #[repr(transparent)] LL | struct Nanoseconds(NanoI32); | ^^^^^^^ the trait `Ord` is not implemented for `(i32) is 0..=999999999` -error[E0277]: can't compare `(i32) is 0..=999999999` with `_` - --> $DIR/derives_fail.rs:11:20 - | -LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] - | ---------- in this derive macro expansion -LL | #[repr(transparent)] -LL | struct Nanoseconds(NanoI32); - | ^^^^^^^ no implementation for `(i32) is 0..=999999999 < _` and `(i32) is 0..=999999999 > _` - | - = help: the trait `PartialOrd<_>` is not implemented for `(i32) is 0..=999999999` - error[E0277]: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied --> $DIR/derives_fail.rs:11:20 | @@ -66,7 +55,7 @@ LL | #[repr(transparent)] LL | struct Nanoseconds(NanoI32); | ^^^^^^^ the trait `Default` is not implemented for `(i32) is 0..=999999999` -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0277, E0369. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs b/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs new file mode 100644 index 0000000000000..a94f6244b17e7 --- /dev/null +++ b/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! unit { + () => {{ + () + }}; +} diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs new file mode 100644 index 0000000000000..f84ebd6f9ea50 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs @@ -0,0 +1,16 @@ +// Make sure we don't suggest compatible variants cross macro context. (issue #142359) +//@ aux-crate:ext=suggest-compatible-variants-macro-issue-142359.rs + +extern crate ext; + +use std::ops::ControlFlow; + +fn main() { + let x: Result = Err(1); + + let _ = match x { + Err(r) => ControlFlow::Break(r), + Ok(r) => { ext::unit!() } //~ ERROR `match` arms have incompatible types [E0308] + + }; +} diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr new file mode 100644 index 0000000000000..7344060a91e4e --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr @@ -0,0 +1,24 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:20 + | +LL | let _ = match x { + | _____________- +LL | | Err(r) => ControlFlow::Break(r), + | | --------------------- this is found to be of type `ControlFlow` +LL | | Ok(r) => { ext::unit!() } + | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` +LL | | +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected enum `ControlFlow` + found unit type `()` + = note: this error originates in the macro `ext::unit` (in Nightly builds, run with -Z macro-backtrace for more info) +help: try wrapping the expression in `std::ops::ControlFlow::Continue` + | +LL | Ok(r) => std::ops::ControlFlow::Continue({ ext::unit!() }) + | ++++++++++++++++++++++++++++++++ + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-17904.rs b/tests/ui/where-clauses/where-clause-on-struct-definitions.rs similarity index 77% rename from tests/ui/issues/issue-17904.rs rename to tests/ui/where-clauses/where-clause-on-struct-definitions.rs index fba71f70dd98e..a49df546ac31b 100644 --- a/tests/ui/issues/issue-17904.rs +++ b/tests/ui/where-clauses/where-clause-on-struct-definitions.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ check-pass #![allow(dead_code)] // Test that we can parse where clauses on various forms of tuple diff --git a/tests/ui/issues/issue-17904-2.rs b/tests/ui/where-clauses/where-clause-on-unit-struct.rs similarity index 73% rename from tests/ui/issues/issue-17904-2.rs rename to tests/ui/where-clauses/where-clause-on-unit-struct.rs index 9603da097b1cf..46fdd3737fa70 100644 --- a/tests/ui/issues/issue-17904-2.rs +++ b/tests/ui/where-clauses/where-clause-on-unit-struct.rs @@ -1,3 +1,4 @@ +//! Regression test for . // Test that we can parse a unit struct with a where clause, even if // it leads to an error later on since `T` is unused. diff --git a/tests/ui/issues/issue-17904-2.stderr b/tests/ui/where-clauses/where-clause-on-unit-struct.stderr similarity index 87% rename from tests/ui/issues/issue-17904-2.stderr rename to tests/ui/where-clauses/where-clause-on-unit-struct.stderr index 9965106d1401a..174c2d9e22e8d 100644 --- a/tests/ui/issues/issue-17904-2.stderr +++ b/tests/ui/where-clauses/where-clause-on-unit-struct.stderr @@ -1,5 +1,5 @@ error[E0392]: type parameter `T` is never used - --> $DIR/issue-17904-2.rs:4:12 + --> $DIR/where-clause-on-unit-struct.rs:5:12 | LL | struct Foo where T: Copy; | ^ unused type parameter