diff --git a/Cargo.lock b/Cargo.lock index 39049cd7e2c6f..9894e374a0bf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2558,8 +2558,10 @@ dependencies = [ "serde", "serde_json", "smallvec", + "socket2", "tempfile", "ui_test", + "windows-sys 0.61.2", ] [[package]] @@ -5472,9 +5474,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 712ed41ae7759..55c0bedb9c39c 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -283,7 +283,6 @@ impl StrippedCfgItem { #[derive(StableHash, PrintAttribute)] pub enum Linkage { AvailableExternally, - Common, ExternalWeak, External, Internal, diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index 66fbbc1e3cbf7..b6733e8c1db54 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -265,7 +265,6 @@ impl AttributeKind { Linkage(linkage, _) => match *linkage { crate::Linkage::Internal => false, crate::Linkage::AvailableExternally - | crate::Linkage::Common | crate::Linkage::External | crate::Linkage::ExternalWeak | crate::Linkage::LinkOnceAny diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 8102208ec519d..d244e6c59e5f9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -306,7 +306,8 @@ pub fn parse_cfg_attr( features: Option<&Features>, lint_node_id: ast::NodeId, ) -> Option<(CfgEntry, Vec<(WithTokens, Span)>)> { - match &cfg_attr.get_normal_item().args { + let item = cfg_attr.get_normal_item(); + match &item.args { ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => { check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim); match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| { @@ -316,11 +317,11 @@ pub fn parse_cfg_attr( Err(e) => { let suggestions = CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ); e.with_span_suggestions( - cfg_attr.get_normal_item().span, + item.span, "must be of the form", suggestions, Applicability::HasPlaceholders, @@ -334,25 +335,24 @@ pub fn parse_cfg_attr( } } _ => { - let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = - cfg_attr.get_normal_item().args - { - (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) - } else { - (cfg_attr.get_normal_item().span, AttributeParseErrorReason::ExpectedList) - }; + let (span, reason) = + if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = item.args { + (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) + } else { + (item.span, AttributeParseErrorReason::ExpectedList) + }; sess.dcx().emit_err(AttributeParseError { span, - inner_span: cfg_attr.get_normal_item().span, + inner_span: item.span, template: CFG_ATTR_TEMPLATE, - path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity), + path: AttrPath::from_ast(&item.path, identity), description: ParsedDescription::Attribute, reason, suggestions: diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ), ), @@ -389,13 +389,14 @@ fn parse_cfg_attr_internal<'a>( )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); + let item = attribute.get_normal_item(); let cfg_predicate = AttributeParser::parse_single_args( sess, attribute.span, - attribute.get_normal_item().span, + item.span, attribute.style, AttrPath { segments: attribute.path().into_boxed_slice(), span: attribute.span }, - Some(attribute.get_normal_item().unsafety), + Some(item.unsafety), AttributeSafety::Normal, ParsedDescription::Attribute, pred_span, diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index f4d0fa35e3f47..ed911a63eb7fe 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -685,10 +685,7 @@ impl SingleAttributeParser for LinkageParser { ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ "available_externally", - "common", "extern_weak", - "external", - "internal", "linkonce", "linkonce_odr", "weak", @@ -711,10 +708,7 @@ impl SingleAttributeParser for LinkageParser { // and don't have to be, LLVM treats them as no-ops. let linkage = match value { sym::available_externally => Linkage::AvailableExternally, - sym::common => Linkage::Common, sym::extern_weak => Linkage::ExternalWeak, - sym::external => Linkage::External, - sym::internal => Linkage::Internal, sym::linkonce => Linkage::LinkOnceAny, sym::linkonce_odr => Linkage::LinkOnceODR, sym::weak => Linkage::WeakAny, @@ -725,10 +719,7 @@ impl SingleAttributeParser for LinkageParser { name_value.value_span, &[ sym::available_externally, - sym::common, sym::extern_weak, - sym::external, - sym::internal, sym::linkonce, sym::linkonce_odr, sym::weak, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index fb4a32d864fbe..a43cd918c8f35 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -1235,7 +1235,7 @@ pub(crate) struct UnknownVersionLiteral { #[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "unspecified")] pub this: Span, #[note("attribute also specified here")] pub other: Span, @@ -2061,7 +2061,7 @@ pub(crate) struct AdditionalCommaSuggestion { #[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct UnusedDuplicate { - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "unspecified")] pub this: Span, #[note("attribute also specified here")] pub other: Span, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 0a6fb9d5da47a..0e8565aef3f0b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1361,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => (), + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int)) => (), _ => { span_mirbug!( self, @@ -1379,7 +1379,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Int), Some(CastTy::Ptr(_))) => (), _ => { span_mirbug!( self, @@ -1396,7 +1396,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => (), + (Some(from), Some(to)) if from.is_int_like() && to.is_int_like() => (), _ => { span_mirbug!( self, @@ -1413,7 +1413,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Float)) => (), + (Some(CastTy::Int), Some(CastTy::Float)) => (), _ => { span_mirbug!( self, @@ -1430,7 +1430,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Float), Some(CastTy::Int(_))) => (), + (Some(CastTy::Float), Some(CastTy::Int)) => (), _ => { span_mirbug!( self, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e52da1136dd64..d4817d7ad25ac 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -339,7 +339,9 @@ fn make_format_args( parse::Suggestion::UsePositional => { let captured_arg_span = fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); - if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) { + if is_source_literal + && let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) + { let span = match args.unnamed_args().last() { Some(arg) => arg.expr.span, None => fmt_span, @@ -360,17 +362,21 @@ fn make_format_args( } } parse::Suggestion::ReorderFormatParameter(span, replacement) => { - let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = - Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { - span, - replacement, - }); + if is_source_literal { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { + span, + replacement, + }); + } } parse::Suggestion::AddMissingColon(span) => { - let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = - Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); + if is_source_literal { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); + } } parse::Suggestion::UseRustDebugPrintingMacro => { // This targets `println!("{=}", x);` and `println!("{0=}", x);` diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt new file mode 100644 index 0000000000000..3a028cd380029 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt @@ -0,0 +1,301 @@ +vaba_s16 +vaba_s32 +vaba_s8 +vaba_u16 +vaba_u32 +vaba_u8 +vabaq_s16 +vabaq_s32 +vabaq_s8 +vabaq_u16 +vabaq_u32 +vabaq_u8 +vhadd_s16 +vhadd_s32 +vhadd_s8 +vhadd_u16 +vhadd_u32 +vhadd_u8 +vhaddq_s16 +vhaddq_s32 +vhaddq_s8 +vhaddq_u16 +vhaddq_u32 +vhaddq_u8 +vhsub_s16 +vhsub_s32 +vhsub_s8 +vhsub_u16 +vhsub_u32 +vhsub_u8 +vhsubq_s16 +vhsubq_s32 +vhsubq_s8 +vhsubq_u16 +vhsubq_u32 +vhsubq_u8 +vld1_f16_x2 +vld1_f16_x3 +vld1_f16_x4 +vld1_f32_x2 +vld1_f32_x3 +vld1_f32_x4 +vld1_f64_x2 +vld1_f64_x3 +vld1_f64_x4 +vld1_p16_x2 +vld1_p16_x3 +vld1_p16_x4 +vld1_p64_x2 +vld1_p64_x3 +vld1_p64_x4 +vld1_p8_x2 +vld1_p8_x3 +vld1_p8_x4 +vld1_s16_x2 +vld1_s16_x3 +vld1_s16_x4 +vld1_s32_x2 +vld1_s32_x3 +vld1_s32_x4 +vld1_s64_x2 +vld1_s64_x3 +vld1_s64_x4 +vld1_s8_x2 +vld1_s8_x3 +vld1_s8_x4 +vld1_u16_x2 +vld1_u16_x3 +vld1_u16_x4 +vld1_u32_x2 +vld1_u32_x3 +vld1_u32_x4 +vld1_u64_x2 +vld1_u64_x3 +vld1_u64_x4 +vld1_u8_x2 +vld1_u8_x3 +vld1_u8_x4 +vld1q_f16_x2 +vld1q_f16_x3 +vld1q_f16_x4 +vld1q_f32_x2 +vld1q_f32_x3 +vld1q_f32_x4 +vld1q_f64_x2 +vld1q_f64_x3 +vld1q_f64_x4 +vld1q_p16_x2 +vld1q_p16_x3 +vld1q_p16_x4 +vld1q_p64_x2 +vld1q_p64_x3 +vld1q_p64_x4 +vld1q_p8_x2 +vld1q_p8_x3 +vld1q_p8_x4 +vld1q_s16_x2 +vld1q_s16_x3 +vld1q_s16_x4 +vld1q_s32_x2 +vld1q_s32_x3 +vld1q_s32_x4 +vld1q_s64_x2 +vld1q_s64_x3 +vld1q_s64_x4 +vld1q_s8_x2 +vld1q_s8_x3 +vld1q_s8_x4 +vld1q_u16_x2 +vld1q_u16_x3 +vld1q_u16_x4 +vld1q_u32_x2 +vld1q_u32_x3 +vld1q_u32_x4 +vld1q_u64_x2 +vld1q_u64_x3 +vld1q_u64_x4 +vld1q_u8_x2 +vld1q_u8_x3 +vld1q_u8_x4 +vld2_f16 +vld2_f32 +vld2_lane_f16 +vld2_lane_f32 +vld2_lane_s16 +vld2_lane_s32 +vld2_lane_s64 +vld2_lane_s8 +vld2_lane_u16 +vld2_lane_u32 +vld2_lane_u64 +vld2_lane_u8 +vld2_p16 +vld2_p8 +vld2_s16 +vld2_s32 +vld2_s64 +vld2_s8 +vld2_u16 +vld2_u32 +vld2_u64 +vld2_u8 +vld2q_f16 +vld2q_f32 +vld2q_f64 +vld2q_lane_f16 +vld2q_lane_f32 +vld2q_lane_s16 +vld2q_lane_s32 +vld2q_lane_s64 +vld2q_lane_s8 +vld2q_lane_u16 +vld2q_lane_u32 +vld2q_lane_u64 +vld2q_lane_u8 +vld2q_p16 +vld2q_p8 +vld2q_s16 +vld2q_s32 +vld2q_s64 +vld2q_s8 +vld2q_u16 +vld2q_u32 +vld2q_u64 +vld2q_u8 +vld3_f16 +vld3_lane_f16 +vld3_lane_f32 +vld3_lane_s16 +vld3_lane_s32 +vld3_lane_s64 +vld3_lane_s8 +vld3_lane_u16 +vld3_lane_u32 +vld3_lane_u64 +vld3_lane_u8 +vld3q_f16 +vld3q_lane_f16 +vld3q_lane_f32 +vld3q_lane_s16 +vld3q_lane_s32 +vld3q_lane_s64 +vld3q_lane_s8 +vld3q_lane_u16 +vld3q_lane_u32 +vld3q_lane_u64 +vld3q_lane_u8 +vld4_f16 +vld4_lane_f16 +vld4_lane_f32 +vld4_lane_s16 +vld4_lane_s32 +vld4_lane_s64 +vld4_lane_s8 +vld4_lane_u16 +vld4_lane_u32 +vld4_lane_u64 +vld4_lane_u8 +vld4q_f16 +vld4q_lane_f16 +vld4q_lane_f32 +vld4q_lane_s16 +vld4q_lane_s32 +vld4q_lane_s64 +vld4q_lane_s8 +vld4q_lane_u16 +vld4q_lane_u32 +vld4q_lane_u64 +vld4q_lane_u8 +vqtbl2_p8 +vqtbl2_s8 +vqtbl2_u8 +vqtbl2q_p8 +vqtbl2q_s8 +vqtbl2q_u8 +vqtbl3_p8 +vqtbl3_s8 +vqtbl3_u8 +vqtbl3q_p8 +vqtbl3q_s8 +vqtbl3q_u8 +vqtbl4_p8 +vqtbl4_s8 +vqtbl4_u8 +vqtbl4q_p8 +vqtbl4q_s8 +vqtbl4q_u8 +vqtbx1_p8 +vqtbx1_s8 +vqtbx1_u8 +vqtbx1q_p8 +vqtbx1q_s8 +vqtbx1q_u8 +vqtbx2_p8 +vqtbx2_s8 +vqtbx2_u8 +vqtbx2q_p8 +vqtbx2q_s8 +vqtbx2q_u8 +vqtbx3_p8 +vqtbx3_s8 +vqtbx3_u8 +vqtbx3q_p8 +vqtbx3q_s8 +vqtbx3q_u8 +vqtbx4_p8 +vqtbx4_s8 +vqtbx4_u8 +vqtbx4q_p8 +vqtbx4q_s8 +vqtbx4q_u8 +vrhadd_s16 +vrhadd_s32 +vrhadd_s8 +vrhadd_u16 +vrhadd_u32 +vrhadd_u8 +vrhaddq_s16 +vrhaddq_s32 +vrhaddq_s8 +vrhaddq_u16 +vrhaddq_u32 +vrhaddq_u8 +vsli_n_p16 +vsli_n_p8 +vsli_n_s16 +vsli_n_s32 +vsli_n_s64 +vsli_n_s8 +vsli_n_u16 +vsli_n_u32 +vsli_n_u64 +vsli_n_u8 +vsliq_n_p16 +vsliq_n_p8 +vsliq_n_s16 +vsliq_n_s32 +vsliq_n_s64 +vsliq_n_s8 +vsliq_n_u16 +vsliq_n_u32 +vsliq_n_u64 +vsliq_n_u8 +vtbl3_p8 +vtbl3_s8 +vtbl3_u8 +vtbl4_p8 +vtbl4_s8 +vtbl4_u8 +vtbx1_p8 +vtbx1_s8 +vtbx1_u8 +vtbx2_p8 +vtbx2_s8 +vtbx2_u8 +vtbx3_p8 +vtbx3_s8 +vtbx3_u8 +vtbx4_p8 +vtbx4_s8 +vtbx4_u8 diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt new file mode 100644 index 0000000000000..5f11867fd3ee9 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt @@ -0,0 +1 @@ +vcvth_s16_f16 diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt new file mode 100644 index 0000000000000..a452f0c16b593 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt @@ -0,0 +1,1173 @@ +__jcvt +vaba_s16 +vaba_s32 +vaba_s8 +vaba_u16 +vaba_u32 +vaba_u8 +vabal_high_s16 +vabal_high_s32 +vabal_high_s8 +vabal_high_u16 +vabal_high_u32 +vabal_high_u8 +vabal_s16 +vabal_s32 +vabal_s8 +vabal_u16 +vabal_u32 +vabal_u8 +vabaq_s16 +vabaq_s32 +vabaq_s8 +vabaq_u16 +vabaq_u32 +vabaq_u8 +vabd_f16 +vabd_f32 +vabd_f64 +vabd_s16 +vabd_s32 +vabd_s8 +vabd_u16 +vabd_u32 +vabd_u8 +vabdd_f64 +vabdh_f16 +vabdl_high_s16 +vabdl_high_s32 +vabdl_high_s8 +vabdl_high_u16 +vabdl_high_u32 +vabdl_high_u8 +vabdl_s16 +vabdl_s32 +vabdl_s8 +vabdl_u16 +vabdl_u32 +vabdl_u8 +vabdq_f16 +vabdq_f32 +vabdq_f64 +vabdq_s16 +vabdq_s32 +vabdq_s8 +vabdq_u16 +vabdq_u32 +vabdq_u8 +vabds_f32 +vabsd_s64 +vaddlv_s16 +vaddlv_s32 +vaddlv_s8 +vaddlv_u16 +vaddlv_u32 +vaddlv_u8 +vaddlvq_s16 +vaddlvq_s32 +vaddlvq_s8 +vaddlvq_u16 +vaddlvq_u32 +vaddvq_f64 +vamax_f16 +vamaxq_f16 +vamin_f16 +vaminq_f16 +vcadd_rot270_f16 +vcadd_rot270_f32 +vcadd_rot90_f16 +vcadd_rot90_f32 +vcaddq_rot270_f16 +vcaddq_rot270_f32 +vcaddq_rot270_f64 +vcaddq_rot90_f16 +vcaddq_rot90_f32 +vcaddq_rot90_f64 +vcage_f16 +vcage_f32 +vcage_f64 +vcaged_f64 +vcageh_f16 +vcageq_f16 +vcageq_f32 +vcageq_f64 +vcages_f32 +vcagt_f16 +vcagt_f32 +vcagt_f64 +vcagtd_f64 +vcagth_f16 +vcagtq_f16 +vcagtq_f32 +vcagtq_f64 +vcagts_f32 +vcale_f16 +vcale_f32 +vcale_f64 +vcaled_f64 +vcaleh_f16 +vcaleq_f16 +vcaleq_f32 +vcaleq_f64 +vcales_f32 +vcalt_f16 +vcalt_f32 +vcalt_f64 +vcaltd_f64 +vcalth_f16 +vcaltq_f16 +vcaltq_f32 +vcaltq_f64 +vcalts_f32 +vcmla_f16 +vcmla_f32 +vcmla_lane_f16 +vcmla_lane_f32 +vcmla_laneq_f16 +vcmla_laneq_f32 +vcmla_rot180_f16 +vcmla_rot180_f32 +vcmla_rot180_lane_f16 +vcmla_rot180_lane_f32 +vcmla_rot180_laneq_f16 +vcmla_rot180_laneq_f32 +vcmla_rot270_f16 +vcmla_rot270_f32 +vcmla_rot270_lane_f16 +vcmla_rot270_lane_f32 +vcmla_rot270_laneq_f16 +vcmla_rot270_laneq_f32 +vcmla_rot90_f16 +vcmla_rot90_f32 +vcmla_rot90_lane_f16 +vcmla_rot90_lane_f32 +vcmla_rot90_laneq_f16 +vcmla_rot90_laneq_f32 +vcmlaq_f16 +vcmlaq_f32 +vcmlaq_f64 +vcmlaq_lane_f16 +vcmlaq_lane_f32 +vcmlaq_laneq_f16 +vcmlaq_laneq_f32 +vcmlaq_rot180_f16 +vcmlaq_rot180_f32 +vcmlaq_rot180_f64 +vcmlaq_rot180_lane_f16 +vcmlaq_rot180_lane_f32 +vcmlaq_rot180_laneq_f16 +vcmlaq_rot180_laneq_f32 +vcmlaq_rot270_f16 +vcmlaq_rot270_f32 +vcmlaq_rot270_f64 +vcmlaq_rot270_lane_f16 +vcmlaq_rot270_lane_f32 +vcmlaq_rot270_laneq_f16 +vcmlaq_rot270_laneq_f32 +vcmlaq_rot90_f16 +vcmlaq_rot90_f32 +vcmlaq_rot90_f64 +vcmlaq_rot90_lane_f16 +vcmlaq_rot90_lane_f32 +vcmlaq_rot90_laneq_f16 +vcmlaq_rot90_laneq_f32 +vcvt_n_f16_s16 +vcvt_n_f16_u16 +vcvt_n_f32_s32 +vcvt_n_f32_u32 +vcvt_n_f64_s64 +vcvt_n_f64_u64 +vcvt_n_s16_f16 +vcvt_n_s32_f32 +vcvt_n_s64_f64 +vcvt_n_u16_f16 +vcvt_n_u32_f32 +vcvt_n_u64_f64 +vcvt_s32_f32 +vcvt_s64_f64 +vcvt_u32_f32 +vcvt_u64_f64 +vcvta_s16_f16 +vcvta_s32_f32 +vcvta_s64_f64 +vcvta_u16_f16 +vcvta_u32_f32 +vcvta_u64_f64 +vcvtad_s64_f64 +vcvtad_u64_f64 +vcvtah_s16_f16 +vcvtah_s32_f16 +vcvtah_s64_f16 +vcvtah_u16_f16 +vcvtah_u32_f16 +vcvtah_u64_f16 +vcvtaq_s16_f16 +vcvtaq_s32_f32 +vcvtaq_s64_f64 +vcvtaq_u16_f16 +vcvtaq_u32_f32 +vcvtaq_u64_f64 +vcvtas_s32_f32 +vcvtas_u32_f32 +vcvtd_n_f64_s64 +vcvtd_n_f64_u64 +vcvtd_n_s64_f64 +vcvtd_n_u64_f64 +vcvth_n_f16_s16 +vcvth_n_f16_s32 +vcvth_n_f16_s64 +vcvth_n_f16_u16 +vcvth_n_f16_u32 +vcvth_n_f16_u64 +vcvth_n_s16_f16 +vcvth_n_s32_f16 +vcvth_n_s64_f16 +vcvth_n_u16_f16 +vcvth_n_u32_f16 +vcvth_n_u64_f16 +vcvtm_s16_f16 +vcvtm_s32_f32 +vcvtm_s64_f64 +vcvtm_u16_f16 +vcvtm_u32_f32 +vcvtm_u64_f64 +vcvtmd_s64_f64 +vcvtmd_u64_f64 +vcvtmh_s16_f16 +vcvtmh_s32_f16 +vcvtmh_s64_f16 +vcvtmh_u16_f16 +vcvtmh_u32_f16 +vcvtmh_u64_f16 +vcvtmq_s16_f16 +vcvtmq_s32_f32 +vcvtmq_s64_f64 +vcvtmq_u16_f16 +vcvtmq_u32_f32 +vcvtmq_u64_f64 +vcvtms_s32_f32 +vcvtms_u32_f32 +vcvtn_s16_f16 +vcvtn_s32_f32 +vcvtn_s64_f64 +vcvtn_u16_f16 +vcvtn_u32_f32 +vcvtn_u64_f64 +vcvtnd_s64_f64 +vcvtnd_u64_f64 +vcvtnh_s16_f16 +vcvtnh_s32_f16 +vcvtnh_s64_f16 +vcvtnh_u16_f16 +vcvtnh_u32_f16 +vcvtnh_u64_f16 +vcvtnq_s16_f16 +vcvtnq_s64_f64 +vcvtnq_u16_f16 +vcvtnq_u32_f32 +vcvtnq_u64_f64 +vcvtns_s32_f32 +vcvtns_u32_f32 +vcvtp_s16_f16 +vcvtp_s32_f32 +vcvtp_s64_f64 +vcvtp_u16_f16 +vcvtp_u32_f32 +vcvtp_u64_f64 +vcvtpd_s64_f64 +vcvtpd_u64_f64 +vcvtph_s16_f16 +vcvtph_s32_f16 +vcvtph_s64_f16 +vcvtph_u16_f16 +vcvtph_u32_f16 +vcvtph_u64_f16 +vcvtpq_s16_f16 +vcvtpq_s32_f32 +vcvtpq_s64_f64 +vcvtpq_u16_f16 +vcvtpq_u32_f32 +vcvtpq_u64_f64 +vcvtps_s32_f32 +vcvtps_u32_f32 +vcvtq_n_f16_s16 +vcvtq_n_f16_u16 +vcvtq_n_f32_s32 +vcvtq_n_f32_u32 +vcvtq_n_f64_s64 +vcvtq_n_f64_u64 +vcvtq_n_s16_f16 +vcvtq_n_s32_f32 +vcvtq_n_s64_f64 +vcvtq_n_u16_f16 +vcvtq_n_u32_f32 +vcvtq_n_u64_f64 +vcvtq_s64_f64 +vcvtq_u32_f32 +vcvtq_u64_f64 +vcvts_n_f32_s32 +vcvts_n_f32_u32 +vcvts_n_s32_f32 +vcvts_n_u32_f32 +vcvtx_f32_f64 +vcvtx_high_f32_f64 +vcvtxd_f32_f64 +vdot_lane_s32 +vdot_lane_u32 +vdot_laneq_s32 +vdot_laneq_u32 +vdot_s32 +vdot_u32 +vdotq_lane_s32 +vdotq_lane_u32 +vdotq_laneq_s32 +vdotq_laneq_u32 +vdotq_s32 +vdotq_u32 +vfmlal_high_f16 +vfmlal_lane_high_f16 +vfmlal_lane_low_f16 +vfmlal_laneq_high_f16 +vfmlal_laneq_low_f16 +vfmlal_low_f16 +vfmlalq_high_f16 +vfmlalq_lane_high_f16 +vfmlalq_lane_low_f16 +vfmlalq_laneq_high_f16 +vfmlalq_laneq_low_f16 +vfmlalq_low_f16 +vfmlsl_high_f16 +vfmlsl_lane_high_f16 +vfmlsl_lane_low_f16 +vfmlsl_laneq_high_f16 +vfmlsl_laneq_low_f16 +vfmlsl_low_f16 +vfmlslq_high_f16 +vfmlslq_lane_high_f16 +vfmlslq_lane_low_f16 +vfmlslq_laneq_high_f16 +vfmlslq_laneq_low_f16 +vfmlslq_low_f16 +vhadd_s16 +vhadd_s32 +vhadd_s8 +vhadd_u16 +vhadd_u32 +vhadd_u8 +vhaddq_s16 +vhaddq_s32 +vhaddq_s8 +vhaddq_u16 +vhaddq_u32 +vhaddq_u8 +vhsub_s16 +vhsub_s32 +vhsub_s8 +vhsub_u16 +vhsub_u32 +vhsub_u8 +vhsubq_s16 +vhsubq_s32 +vhsubq_s8 +vhsubq_u16 +vhsubq_u32 +vhsubq_u8 +vld1_dup_f16 +vld1_f16 +vld1_f16_x2 +vld1_f16_x3 +vld1_f16_x4 +vld1_lane_f16 +vld1q_dup_f16 +vld1q_f16 +vld1q_f16_x2 +vld1q_f16_x3 +vld1q_f16_x4 +vld1q_lane_f16 +vld2_dup_f16 +vld2_f16 +vld2_lane_f16 +vld2q_dup_f16 +vld2q_f16 +vld2q_lane_f16 +vld3_dup_f16 +vld3_f16 +vld3_lane_f16 +vld3q_dup_f16 +vld3q_f16 +vld3q_lane_f16 +vld4_dup_f16 +vld4_f16 +vld4_lane_f16 +vld4q_dup_f16 +vld4q_f16 +vld4q_lane_f16 +vluti2_lane_f16 +vluti2_laneq_f16 +vluti2q_lane_f16 +vluti2q_laneq_f16 +vluti4q_lane_f16_x2 +vluti4q_laneq_f16_x2 +vmax_f16 +vmaxh_f16 +vmaxnm_f16 +vmaxnm_f32 +vmaxnm_f64 +vmaxnmh_f16 +vmaxnmq_f16 +vmaxnmq_f32 +vmaxnmq_f64 +vmaxnmv_f16 +vmaxnmv_f32 +vmaxnmvq_f16 +vmaxnmvq_f32 +vmaxnmvq_f64 +vmaxq_f16 +vmaxv_f16 +vmaxv_f32 +vmaxvq_f16 +vmaxvq_f32 +vmaxvq_f64 +vmin_f16 +vminh_f16 +vminnm_f16 +vminnm_f32 +vminnm_f64 +vminnmh_f16 +vminnmq_f16 +vminnmq_f32 +vminnmq_f64 +vminnmv_f16 +vminnmv_f32 +vminnmvq_f16 +vminnmvq_f32 +vminnmvq_f64 +vminq_f16 +vminv_f16 +vminv_f32 +vminvq_f16 +vminvq_f32 +vminvq_f64 +vmmlaq_s32 +vmmlaq_u32 +vmul_p8 +vmulq_p8 +vmulx_f16 +vmulx_f32 +vmulx_f64 +vmulx_lane_f16 +vmulx_lane_f32 +vmulx_lane_f64 +vmulx_laneq_f16 +vmulx_laneq_f32 +vmulx_laneq_f64 +vmulx_n_f16 +vmulxd_f64 +vmulxd_lane_f64 +vmulxd_laneq_f64 +vmulxh_f16 +vmulxh_lane_f16 +vmulxh_laneq_f16 +vmulxq_f16 +vmulxq_f32 +vmulxq_f64 +vmulxq_lane_f16 +vmulxq_lane_f32 +vmulxq_lane_f64 +vmulxq_laneq_f16 +vmulxq_laneq_f32 +vmulxq_laneq_f64 +vmulxq_n_f16 +vmulxs_f32 +vmulxs_lane_f32 +vmulxs_laneq_f32 +vpadd_f16 +vpadd_f32 +vpaddq_f16 +vpmax_f16 +vpmaxnm_f16 +vpmaxnm_f32 +vpmaxnmq_f16 +vpmaxnmq_f32 +vpmaxnmq_f64 +vpmaxnmqd_f64 +vpmaxnms_f32 +vpmaxq_f16 +vpmaxqd_f64 +vpmaxs_f32 +vpmin_f16 +vpminnm_f16 +vpminnm_f32 +vpminnmq_f16 +vpminnmq_f32 +vpminnmq_f64 +vpminnmqd_f64 +vpminnms_f32 +vpminq_f16 +vpminqd_f64 +vpmins_f32 +vqabs_s16 +vqabs_s32 +vqabs_s64 +vqabs_s8 +vqabsb_s8 +vqabsd_s64 +vqabsh_s16 +vqabsq_s16 +vqabsq_s32 +vqabsq_s64 +vqabsq_s8 +vqabss_s32 +vqaddd_s64 +vqaddd_u64 +vqadds_s32 +vqadds_u32 +vqdmlal_high_lane_s16 +vqdmlal_high_lane_s32 +vqdmlal_high_laneq_s16 +vqdmlal_high_laneq_s32 +vqdmlal_high_n_s16 +vqdmlal_high_n_s32 +vqdmlal_high_s16 +vqdmlal_high_s32 +vqdmlal_lane_s16 +vqdmlal_lane_s32 +vqdmlal_laneq_s16 +vqdmlal_laneq_s32 +vqdmlal_n_s16 +vqdmlal_n_s32 +vqdmlal_s16 +vqdmlal_s32 +vqdmlalh_lane_s16 +vqdmlalh_laneq_s16 +vqdmlalh_s16 +vqdmlals_lane_s32 +vqdmlals_laneq_s32 +vqdmlals_s32 +vqdmlsl_high_lane_s16 +vqdmlsl_high_lane_s32 +vqdmlsl_high_laneq_s16 +vqdmlsl_high_laneq_s32 +vqdmlsl_high_n_s16 +vqdmlsl_high_n_s32 +vqdmlsl_high_s16 +vqdmlsl_high_s32 +vqdmlsl_lane_s16 +vqdmlsl_lane_s32 +vqdmlsl_laneq_s16 +vqdmlsl_laneq_s32 +vqdmlsl_n_s16 +vqdmlsl_n_s32 +vqdmlsl_s16 +vqdmlsl_s32 +vqdmlslh_lane_s16 +vqdmlslh_laneq_s16 +vqdmlslh_s16 +vqdmlsls_lane_s32 +vqdmlsls_laneq_s32 +vqdmlsls_s32 +vqdmull_high_lane_s16 +vqdmull_high_lane_s32 +vqdmull_high_laneq_s16 +vqdmull_high_laneq_s32 +vqdmull_high_n_s16 +vqdmull_high_n_s32 +vqdmull_high_s16 +vqdmull_high_s32 +vqdmull_lane_s16 +vqdmull_lane_s32 +vqdmull_laneq_s16 +vqdmull_laneq_s32 +vqdmull_n_s16 +vqdmull_n_s32 +vqdmull_s16 +vqdmull_s32 +vqdmullh_lane_s16 +vqdmullh_laneq_s16 +vqdmullh_s16 +vqdmulls_lane_s32 +vqdmulls_laneq_s32 +vqdmulls_s32 +vqmovn_high_s16 +vqmovn_high_s32 +vqmovn_high_s64 +vqmovn_high_u16 +vqmovn_high_u32 +vqmovn_high_u64 +vqmovn_s16 +vqmovn_s32 +vqmovn_s64 +vqmovn_u16 +vqmovn_u32 +vqmovn_u64 +vqmovnd_s64 +vqmovnd_u64 +vqmovnh_s16 +vqmovnh_u16 +vqmovns_s32 +vqmovns_u32 +vqmovun_high_s16 +vqmovun_high_s32 +vqmovun_high_s64 +vqmovun_s16 +vqmovun_s32 +vqmovun_s64 +vqmovund_s64 +vqmovunh_s16 +vqmovuns_s32 +vqneg_s16 +vqneg_s32 +vqneg_s64 +vqneg_s8 +vqnegb_s8 +vqnegd_s64 +vqnegh_s16 +vqnegq_s16 +vqnegq_s32 +vqnegq_s64 +vqnegq_s8 +vqnegs_s32 +vqrdmlah_lane_s16 +vqrdmlah_lane_s32 +vqrdmlah_laneq_s16 +vqrdmlah_laneq_s32 +vqrdmlah_s16 +vqrdmlah_s32 +vqrdmlahh_lane_s16 +vqrdmlahh_laneq_s16 +vqrdmlahh_s16 +vqrdmlahq_lane_s16 +vqrdmlahq_lane_s32 +vqrdmlahq_laneq_s16 +vqrdmlahq_laneq_s32 +vqrdmlahq_s16 +vqrdmlahq_s32 +vqrdmlahs_lane_s32 +vqrdmlahs_laneq_s32 +vqrdmlahs_s32 +vqrdmlsh_lane_s16 +vqrdmlsh_lane_s32 +vqrdmlsh_laneq_s16 +vqrdmlsh_laneq_s32 +vqrdmlsh_s16 +vqrdmlsh_s32 +vqrdmlshh_lane_s16 +vqrdmlshh_laneq_s16 +vqrdmlshh_s16 +vqrdmlshq_lane_s16 +vqrdmlshq_lane_s32 +vqrdmlshq_laneq_s16 +vqrdmlshq_laneq_s32 +vqrdmlshq_s16 +vqrdmlshq_s32 +vqrdmlshs_lane_s32 +vqrdmlshs_laneq_s32 +vqrdmlshs_s32 +vqrdmulh_lane_s16 +vqrdmulh_lane_s32 +vqrdmulh_laneq_s16 +vqrdmulh_laneq_s32 +vqrdmulh_n_s16 +vqrdmulh_n_s32 +vqrdmulh_s16 +vqrdmulh_s32 +vqrdmulhh_lane_s16 +vqrdmulhh_laneq_s16 +vqrdmulhh_s16 +vqrdmulhq_lane_s16 +vqrdmulhq_lane_s32 +vqrdmulhq_laneq_s16 +vqrdmulhq_laneq_s32 +vqrdmulhq_n_s16 +vqrdmulhq_n_s32 +vqrdmulhq_s16 +vqrdmulhq_s32 +vqrdmulhs_lane_s32 +vqrdmulhs_laneq_s32 +vqrdmulhs_s32 +vqrshl_s16 +vqrshl_s32 +vqrshl_s64 +vqrshl_s8 +vqrshl_u16 +vqrshl_u32 +vqrshl_u64 +vqrshl_u8 +vqrshlb_s8 +vqrshlb_u8 +vqrshld_s64 +vqrshld_u64 +vqrshlh_s16 +vqrshlh_u16 +vqrshlq_s16 +vqrshlq_s32 +vqrshlq_s64 +vqrshlq_s8 +vqrshlq_u16 +vqrshlq_u32 +vqrshlq_u64 +vqrshlq_u8 +vqrshls_s32 +vqrshls_u32 +vqrshrn_high_n_s16 +vqrshrn_high_n_s32 +vqrshrn_high_n_s64 +vqrshrn_high_n_u16 +vqrshrn_high_n_u32 +vqrshrn_high_n_u64 +vqrshrn_n_s16 +vqrshrn_n_s32 +vqrshrn_n_s64 +vqrshrn_n_u16 +vqrshrn_n_u32 +vqrshrn_n_u64 +vqrshrnd_n_s64 +vqrshrnd_n_u64 +vqrshrnh_n_s16 +vqrshrnh_n_u16 +vqrshrns_n_s32 +vqrshrns_n_u32 +vqrshrun_high_n_s16 +vqrshrun_high_n_s32 +vqrshrun_high_n_s64 +vqrshrun_n_s16 +vqrshrun_n_s32 +vqrshrun_n_s64 +vqrshrund_n_s64 +vqrshrunh_n_s16 +vqrshruns_n_s32 +vqshl_n_s16 +vqshl_n_s32 +vqshl_n_s64 +vqshl_n_s8 +vqshl_n_u16 +vqshl_n_u32 +vqshl_n_u64 +vqshl_n_u8 +vqshl_s16 +vqshl_s32 +vqshl_s64 +vqshl_s8 +vqshl_u16 +vqshl_u32 +vqshl_u64 +vqshl_u8 +vqshlb_n_s8 +vqshlb_n_u8 +vqshlb_s8 +vqshlb_u8 +vqshld_n_s64 +vqshld_n_u64 +vqshld_s64 +vqshld_u64 +vqshlh_n_s16 +vqshlh_n_u16 +vqshlh_s16 +vqshlh_u16 +vqshlq_n_s16 +vqshlq_n_s32 +vqshlq_n_s64 +vqshlq_n_s8 +vqshlq_n_u16 +vqshlq_n_u32 +vqshlq_n_u64 +vqshlq_n_u8 +vqshlq_s16 +vqshlq_s32 +vqshlq_s64 +vqshlq_s8 +vqshlq_u16 +vqshlq_u32 +vqshlq_u64 +vqshlq_u8 +vqshls_n_s32 +vqshls_n_u32 +vqshls_s32 +vqshls_u32 +vqshlu_n_s16 +vqshlu_n_s32 +vqshlu_n_s64 +vqshlu_n_s8 +vqshlub_n_s8 +vqshlud_n_s64 +vqshluh_n_s16 +vqshluq_n_s16 +vqshluq_n_s32 +vqshluq_n_s64 +vqshluq_n_s8 +vqshlus_n_s32 +vqshrn_high_n_s16 +vqshrn_high_n_s32 +vqshrn_high_n_s64 +vqshrn_high_n_u16 +vqshrn_high_n_u32 +vqshrn_high_n_u64 +vqshrn_n_s16 +vqshrn_n_s32 +vqshrn_n_s64 +vqshrn_n_u16 +vqshrn_n_u32 +vqshrn_n_u64 +vqshrnd_n_s64 +vqshrnd_n_u64 +vqshrnh_n_s16 +vqshrnh_n_u16 +vqshrns_n_s32 +vqshrns_n_u32 +vqshrun_high_n_s16 +vqshrun_high_n_s32 +vqshrun_high_n_s64 +vqshrun_n_s16 +vqshrun_n_s32 +vqshrun_n_s64 +vqshrund_n_s64 +vqshrunh_n_s16 +vqshruns_n_s32 +vqsubd_s64 +vqsubd_u64 +vqsubs_s32 +vqsubs_u32 +vqtbl2_p8 +vqtbl2_s8 +vqtbl2_u8 +vqtbl2q_p8 +vqtbl2q_s8 +vqtbl2q_u8 +vqtbl3_p8 +vqtbl3_s8 +vqtbl3_u8 +vqtbl3q_p8 +vqtbl3q_s8 +vqtbl3q_u8 +vqtbl4_p8 +vqtbl4_s8 +vqtbl4_u8 +vqtbl4q_p8 +vqtbl4q_s8 +vqtbl4q_u8 +vqtbx1_p8 +vqtbx1_s8 +vqtbx1_u8 +vqtbx1q_p8 +vqtbx1q_s8 +vqtbx1q_u8 +vqtbx2_p8 +vqtbx2_s8 +vqtbx2_u8 +vqtbx2q_p8 +vqtbx2q_s8 +vqtbx2q_u8 +vqtbx3_p8 +vqtbx3_s8 +vqtbx3_u8 +vqtbx3q_p8 +vqtbx3q_s8 +vqtbx3q_u8 +vqtbx4_p8 +vqtbx4_s8 +vqtbx4_u8 +vqtbx4q_p8 +vqtbx4q_s8 +vqtbx4q_u8 +vraddhn_high_s16 +vraddhn_high_s32 +vraddhn_high_s64 +vraddhn_high_u16 +vraddhn_high_u32 +vraddhn_high_u64 +vraddhn_s16 +vraddhn_s32 +vraddhn_s64 +vraddhn_u16 +vraddhn_u32 +vraddhn_u64 +vrecpe_f16 +vrecpe_f32 +vrecpe_f64 +vrecpe_u32 +vrecped_f64 +vrecpeh_f16 +vrecpeq_f16 +vrecpeq_f64 +vrecpeq_u32 +vrecpes_f32 +vrecps_f16 +vrecps_f32 +vrecps_f64 +vrecpsd_f64 +vrecpsh_f16 +vrecpsq_f16 +vrecpsq_f64 +vrecpss_f32 +vrecpxd_f64 +vrecpxh_f16 +vrecpxs_f32 +vreinterpretq_f16_p128 +vrhadd_s16 +vrhadd_s32 +vrhadd_s8 +vrhadd_u16 +vrhadd_u32 +vrhadd_u8 +vrhaddq_s16 +vrhaddq_s32 +vrhaddq_s8 +vrhaddq_u16 +vrhaddq_u32 +vrhaddq_u8 +vrnd32x_f32 +vrnd32x_f64 +vrnd32xq_f32 +vrnd32xq_f64 +vrnd32z_f32 +vrnd32z_f64 +vrnd32zq_f32 +vrnd32zq_f64 +vrnd64x_f32 +vrnd64x_f64 +vrnd64xq_f32 +vrnd64xq_f64 +vrnd64z_f32 +vrnd64z_f64 +vrnd64zq_f32 +vrnd64zq_f64 +vrndi_f16 +vrndi_f32 +vrndi_f64 +vrndih_f16 +vrndiq_f16 +vrndiq_f32 +vrndiq_f64 +vrndn_f16 +vrndnh_f16 +vrndnq_f16 +vrndns_f32 +vrshl_s16 +vrshl_s32 +vrshl_s64 +vrshl_s8 +vrshl_u16 +vrshl_u32 +vrshl_u64 +vrshl_u8 +vrshld_s64 +vrshld_u64 +vrshlq_s16 +vrshlq_s32 +vrshlq_s64 +vrshlq_s8 +vrshlq_u16 +vrshlq_u32 +vrshlq_u64 +vrshlq_u8 +vrshr_n_s16 +vrshr_n_s32 +vrshr_n_s64 +vrshr_n_s8 +vrshr_n_u16 +vrshr_n_u32 +vrshr_n_u64 +vrshr_n_u8 +vrshrd_n_s64 +vrshrd_n_u64 +vrshrn_high_n_s16 +vrshrn_high_n_s32 +vrshrn_high_n_s64 +vrshrn_high_n_u16 +vrshrn_high_n_u32 +vrshrn_high_n_u64 +vrshrn_n_s16 +vrshrn_n_s32 +vrshrn_n_s64 +vrshrn_n_u16 +vrshrn_n_u32 +vrshrn_n_u64 +vrshrq_n_s16 +vrshrq_n_s32 +vrshrq_n_s64 +vrshrq_n_s8 +vrshrq_n_u16 +vrshrq_n_u32 +vrshrq_n_u64 +vrshrq_n_u8 +vrsqrte_f16 +vrsqrte_f32 +vrsqrte_f64 +vrsqrte_u32 +vrsqrted_f64 +vrsqrteh_f16 +vrsqrteq_f16 +vrsqrteq_f32 +vrsqrteq_f64 +vrsqrteq_u32 +vrsqrtes_f32 +vrsqrts_f16 +vrsqrts_f32 +vrsqrts_f64 +vrsqrtsd_f64 +vrsqrtsh_f16 +vrsqrtsq_f16 +vrsqrtsq_f32 +vrsqrtsq_f64 +vrsqrtss_f32 +vrsra_n_s16 +vrsra_n_s32 +vrsra_n_s64 +vrsra_n_s8 +vrsra_n_u16 +vrsra_n_u32 +vrsra_n_u64 +vrsra_n_u8 +vrsrad_n_s64 +vrsrad_n_u64 +vrsraq_n_s16 +vrsraq_n_s32 +vrsraq_n_s64 +vrsraq_n_s8 +vrsraq_n_u16 +vrsraq_n_u32 +vrsraq_n_u64 +vrsraq_n_u8 +vrsubhn_high_s16 +vrsubhn_high_s32 +vrsubhn_high_s64 +vrsubhn_high_u16 +vrsubhn_high_u32 +vrsubhn_high_u64 +vrsubhn_s16 +vrsubhn_s32 +vrsubhn_s64 +vrsubhn_u16 +vrsubhn_u32 +vrsubhn_u64 +vscale_f16 +vscaleq_f16 +vshl_s16 +vshl_s32 +vshl_s64 +vshl_s8 +vshl_u16 +vshl_u32 +vshl_u64 +vshl_u8 +vshld_s64 +vshld_u64 +vshlq_s16 +vshlq_s32 +vshlq_s64 +vshlq_s8 +vshlq_u16 +vshlq_u32 +vshlq_u64 +vshlq_u8 +vsli_n_p16 +vsli_n_p64 +vsli_n_p8 +vsli_n_s16 +vsli_n_s32 +vsli_n_s64 +vsli_n_s8 +vsli_n_u16 +vsli_n_u32 +vsli_n_u64 +vsli_n_u8 +vslid_n_s64 +vslid_n_u64 +vsliq_n_p16 +vsliq_n_p64 +vsliq_n_p8 +vsliq_n_s16 +vsliq_n_s32 +vsliq_n_s64 +vsliq_n_s8 +vsliq_n_u16 +vsliq_n_u32 +vsliq_n_u64 +vsliq_n_u8 +vsm3partw1q_u32 +vsm3partw2q_u32 +vsm3ss1q_u32 +vsm3tt1aq_u32 +vsm3tt1bq_u32 +vsm3tt2aq_u32 +vsm3tt2bq_u32 +vsm4ekeyq_u32 +vsm4eq_u32 +vsqadd_u16 +vsqadd_u32 +vsqadd_u64 +vsqadd_u8 +vsqaddb_u8 +vsqaddd_u64 +vsqaddh_u16 +vsqaddq_u16 +vsqaddq_u32 +vsqaddq_u64 +vsqaddq_u8 +vsqadds_u32 +vst1_f16 +vst1_f16_x2 +vst1_f16_x3 +vst1_f16_x4 +vst1_lane_f16 +vst1q_f16 +vst1q_f16_x2 +vst1q_f16_x3 +vst1q_f16_x4 +vst1q_lane_f16 +vst2_f16 +vst2_lane_f16 +vst2q_f16 +vst2q_lane_f16 +vst3_f16 +vst3_lane_f16 +vst3q_f16 +vst3q_lane_f16 +vst4_f16 +vst4_lane_f16 +vst4q_f16 +vst4q_lane_f16 +vsudot_lane_s32 +vsudot_laneq_s32 +vsudotq_lane_s32 +vsudotq_laneq_s32 +vtbl2_p8 +vtbl2_s8 +vtbl2_u8 +vtbl3_p8 +vtbl3_s8 +vtbl3_u8 +vtbl4_p8 +vtbl4_s8 +vtbl4_u8 +vtbx1_p8 +vtbx1_s8 +vtbx1_u8 +vtbx2_p8 +vtbx2_s8 +vtbx2_u8 +vtbx3_p8 +vtbx3_s8 +vtbx3_u8 +vtbx4_p8 +vtbx4_s8 +vtbx4_u8 +vuqadd_s16 +vuqadd_s32 +vuqadd_s64 +vuqadd_s8 +vuqaddb_s8 +vuqaddd_s64 +vuqaddh_s16 +vuqaddq_s16 +vuqaddq_s32 +vuqaddq_s64 +vuqaddq_s8 +vuqadds_s32 +vusdot_lane_s32 +vusdot_laneq_s32 +vusdot_s32 +vusdotq_lane_s32 +vusdotq_laneq_s32 +vusdotq_s32 +vusmmlaq_s32 diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 5ae76dc1e591e..3b9ae2608abfe 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -1,4 +1,5 @@ use std::ffi::OsStr; +use std::fs; use std::path::PathBuf; use std::process::Command; @@ -108,7 +109,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ }), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), - TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), TestCase::build_bin_and_run("aot.gen_block_iterate", "example/gen_block_iterate.rs", &[]), TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), TestCase::custom("test.sysroot", &|runner| { @@ -182,6 +182,12 @@ static SYSROOT_TESTS_SRC: RelPath = RelPath::build("sysroot_tests"); static SYSROOT_TESTS: CargoProject = CargoProject::new(SYSROOT_TESTS_SRC, "sysroot_tests_target"); +static STDARCH_SRC: RelPath = RelPath::build("stdarch"); + +static STDARCH: CargoProject = CargoProject::new(STDARCH_SRC, "stdarch_target"); +static STDARCH_RUST_PROGRAMS: CargoProject = + CargoProject::new(RelPath::build("stdarch/rust_programs"), "stdarch_rust_programs_target"); + const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ TestCase::custom("test.rust-random/rand", &|runner| { RAND_REPO.patch(&runner.dirs); @@ -283,6 +289,116 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(test_cmd); } }), + TestCase::custom("test.stdarch-core", &|runner| { + let target = runner.target_compiler.target.as_str(); + let (arch, _) = target.split_once('-').unwrap(); + + // FIXME: Add support for remaining architectures. + if !["aarch64"].contains(&arch) { + eprintln!("Skipping `stdarch-core` tests: unsupported target"); + return; + } + + apply_patches( + &runner.dirs, + "stdarch", + &runner.stdlib_source.join("library/stdarch"), + &STDARCH_SRC.to_path(&runner.dirs), + ); + + STDARCH.clean(&runner.dirs); + + let skip = format!("build_system/stdarch_skip/{arch}_core.txt"); + let skip = fs::read_to_string(runner.dirs.source_dir.join(skip)).unwrap_or_default(); + + let skip_functions = skip + .lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) + .collect::>() + .join(","); + + if runner.is_native { + let mut test_cmd = STDARCH.test(&runner.target_compiler, &runner.dirs); + + test_cmd.env("TARGET", target); + test_cmd.env("STDARCH_TEST_SKIP_FUNCTION", &skip_functions); + + test_cmd.args(["-p", "core_arch", "--tests", "--", "-q"]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = STDARCH.build(&runner.target_compiler, &runner.dirs); + + build_cmd.env("TARGET", target); + + build_cmd.args(["-p", "core_arch", "--tests"]); + spawn_and_wait(build_cmd); + } + }), + TestCase::custom("test.stdarch-intrinsics", &|runner| { + let target = runner.target_compiler.target.as_str(); + let (arch, _) = target.split_once('-').unwrap(); + + // FIXME: Add support for remaining architectures. + let input = match arch { + "aarch64" => "intrinsics_data/arm_intrinsics.json", + _ => { + eprintln!("Skipping `stdarch-intrinsics` tests: unsupported target"); + return; + } + }; + + apply_patches( + &runner.dirs, + "stdarch", + &runner.stdlib_source.join("library/stdarch"), + &STDARCH_SRC.to_path(&runner.dirs), + ); + + STDARCH_RUST_PROGRAMS.clean(&runner.dirs); + + if !runner.is_native { + eprintln!("Cross-Compiling: Not running tests"); + return; + } + + let cc = if target.contains("apple") { "clang" } else { "gcc" }; + + let mut generate_cmd = STDARCH.run(&runner.target_compiler, &runner.dirs); + generate_cmd.current_dir(STDARCH.source_dir(&runner.dirs)); + generate_cmd.args([ + "-p", + "intrinsic-test", + "--", + input, + "--target", + target, + "--cc-arg-style", + cc, + "--skip", + &format!("crates/intrinsic-test/missing_{arch}_common.txt"), + "--skip", + &format!("crates/intrinsic-test/missing_{arch}_{cc}.txt"), + ]); + + for skip in [ + format!("build_system/stdarch_skip/{arch}_intrinsics_common.txt"), + format!("build_system/stdarch_skip/{arch}_intrinsics_{cc}.txt"), + ] { + let skip = runner.dirs.source_dir.join(skip); + + if skip.exists() { + generate_cmd.arg("--skip").arg(skip); + } + } + + spawn_and_wait(generate_cmd); + + let mut test_cmd = STDARCH_RUST_PROGRAMS.test(&runner.target_compiler, &runner.dirs); + test_cmd.args(["--workspace", "--tests", "--no-fail-fast", "--", "-q"]); + spawn_and_wait(test_cmd); + }), ]; pub(crate) fn run_tests( diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index ca126f4e39e29..c1068e6257d73 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -22,7 +22,6 @@ aot.float-minmax-pass aot.powi_libcall_signature aot.issue-72793 aot.issue-59326 -aot.neon aot.gen_block_iterate aot.raw-dylib test.sysroot @@ -32,3 +31,5 @@ test.rust-random/rand test.regex test.graviola test.portable-simd +test.stdarch-core +test.stdarch-intrinsics diff --git a/compiler/rustc_codegen_cranelift/example/neon.rs b/compiler/rustc_codegen_cranelift/example/neon.rs deleted file mode 100644 index ba63333daa339..0000000000000 --- a/compiler/rustc_codegen_cranelift/example/neon.rs +++ /dev/null @@ -1,963 +0,0 @@ -// Most of these tests are copied from https://github.com/japaric/stdsimd/blob/0f4413d01c4f0c3ffbc5a69e9a37fbc7235b31a9/coresimd/arm/neon.rs - -#![cfg_attr(target_arch = "aarch64", feature(portable_simd))] - -#[cfg(target_arch = "aarch64")] -use std::arch::aarch64::*; -#[cfg(target_arch = "aarch64")] -use std::mem::transmute; -#[cfg(target_arch = "aarch64")] -use std::simd::*; - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "crc")] -unsafe fn test_crc32() { - assert!(std::arch::is_aarch64_feature_detected!("crc")); - - let a: u32 = 42; - let b: u64 = 0xdeadbeef; - - assert_eq!(__crc32b(a, b as u8), 0xEB0E363F); - assert_eq!(__crc32h(a, b as u16), 0x9A54BD80); - assert_eq!(__crc32w(a, b as u32), 0xF491F059); - assert_eq!(__crc32d(a, b as u64), 0xD14BBEA6); - - assert_eq!(__crc32cb(a, b as u8), 0xF67C32D8); - assert_eq!(__crc32ch(a, b as u16), 0x479108B8); - assert_eq!(__crc32cw(a, b as u32), 0x979F49F8); - assert_eq!(__crc32cd(a, b as u64), 0x0E6BE593); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s8() { - let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); - let b = i8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = i8x8::from([-2, -4, 5, 7, 0, 2, 4, 6]); - let r: i8x8 = unsafe { transmute(vpmin_s8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s16() { - let a = i16x4::from([1, 2, 3, -4]); - let b = i16x4::from([0, 3, 2, 5]); - let e = i16x4::from([1, -4, 0, 2]); - let r: i16x4 = unsafe { transmute(vpmin_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s32() { - let a = i32x2::from([1, -2]); - let b = i32x2::from([0, 3]); - let e = i32x2::from([-2, 0]); - let r: i32x2 = unsafe { transmute(vpmin_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = u8x8::from([1, 3, 5, 7, 0, 2, 4, 6]); - let r: u8x8 = unsafe { transmute(vpmin_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([0, 3, 2, 5]); - let e = u16x4::from([1, 3, 0, 2]); - let r: u16x4 = unsafe { transmute(vpmin_u16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([0, 3]); - let e = u32x2::from([1, 0]); - let r: u32x2 = unsafe { transmute(vpmin_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_f32() { - let a = f32x2::from([1., -2.]); - let b = f32x2::from([0., 3.]); - let e = f32x2::from([-2., 0.]); - let r: f32x2 = unsafe { transmute(vpmin_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s8() { - let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); - let b = i8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = i8x8::from([1, 3, 6, 8, 3, 5, 7, 9]); - let r: i8x8 = unsafe { transmute(vpmax_s8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s16() { - let a = i16x4::from([1, 2, 3, -4]); - let b = i16x4::from([0, 3, 2, 5]); - let e = i16x4::from([2, 3, 3, 5]); - let r: i16x4 = unsafe { transmute(vpmax_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s32() { - let a = i32x2::from([1, -2]); - let b = i32x2::from([0, 3]); - let e = i32x2::from([1, 3]); - let r: i32x2 = unsafe { transmute(vpmax_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = u8x8::from([2, 4, 6, 8, 3, 5, 7, 9]); - let r: u8x8 = unsafe { transmute(vpmax_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([0, 3, 2, 5]); - let e = u16x4::from([2, 4, 3, 5]); - let r: u16x4 = unsafe { transmute(vpmax_u16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([0, 3]); - let e = u32x2::from([2, 3]); - let r: u32x2 = unsafe { transmute(vpmax_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_f32() { - let a = f32x2::from([1., -2.]); - let b = f32x2::from([0., 3.]); - let e = f32x2::from([1., 3.]); - let r: f32x2 = unsafe { transmute(vpmax_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s16() { - let a = i16x4::from([1, 2, 3, 4]); - let b = i16x4::from([0, -1, -2, -3]); - let r: i16x4 = unsafe { transmute(vpadd_s16(transmute(a), transmute(b))) }; - let e = i16x4::from([3, 7, -1, -5]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s32() { - let a = i32x2::from([1, 2]); - let b = i32x2::from([0, -1]); - let r: i32x2 = unsafe { transmute(vpadd_s32(transmute(a), transmute(b))) }; - let e = i32x2::from([3, -1]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s8() { - let a = i8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = i8x8::from([0, -1, -2, -3, -4, -5, -6, -7]); - let r: i8x8 = unsafe { transmute(vpadd_s8(transmute(a), transmute(b))) }; - let e = i8x8::from([3, 7, 11, 15, -1, -5, -9, -13]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([30, 31, 32, 33]); - let r: u16x4 = unsafe { transmute(vpadd_u16(transmute(a), transmute(b))) }; - let e = u16x4::from([3, 7, 61, 65]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([30, 31]); - let r: u32x2 = unsafe { transmute(vpadd_u32(transmute(a), transmute(b))) }; - let e = u32x2::from([3, 61]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([30, 31, 32, 33, 34, 35, 36, 37]); - let r: u8x8 = unsafe { transmute(vpadd_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([3, 7, 11, 15, 61, 65, 69, 73]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqsub_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 0xff]); - let b = u8x8::from([30, 1, 1, 1, 34, 0xff, 36, 37]); - let r: u8x8 = unsafe { transmute(vqsub_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([0, 1, 2, 3, 0, 0, 0, 218]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqadd_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 0xff]); - let b = u8x8::from([30, 1, 1, 1, 34, 0xff, 36, 37]); - let r: u8x8 = unsafe { transmute(vqadd_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([31, 3, 4, 5, 39, 0xff, 43, 0xff]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vmaxq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.fmax.v4f32 - let a = f32x4::from([0., -1., 2., -3.]); - let b = f32x4::from([-4., 5., -6., 7.]); - let e = f32x4::from([0., 5., 2., 7.]); - let r: f32x4 = unsafe { transmute(vmaxq_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vminq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.fmin.v4f32 - let a = f32x4::from([0., -1., 2., -3.]); - let b = f32x4::from([-4., 5., -6., 7.]); - let e = f32x4::from([-4., -1., -6., -3.]); - let r: f32x4 = unsafe { transmute(vminq_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vaddvq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.faddv.f32.v4f32 - let a = f32x4::from([0., 1., 2., 3.]); - let e = 6f32; - let r = unsafe { vaddvq_f32(transmute(a)) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vrndnq_f32() { - // llvm intrinsic: llvm.roundeven.v4f32 - let a = f32x4::from([0.1, -1.9, 4.5, 5.5]); - let e = f32x4::from([0., -2., 4., 6.]); - let r: f32x4 = unsafe { transmute(vrndnq_f32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaeseq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aese - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let e = u8x16::from([ - 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xca, - ]); - let r: u8x16 = unsafe { transmute(vaeseq_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesdq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesd - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let e = u8x16::from([ - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, - ]); - let r: u8x16 = unsafe { transmute(vaesdq_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesmcq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesmc - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let e = u8x16::from([2, 7, 0, 5, 6, 3, 4, 1, 10, 15, 8, 13, 14, 11, 12, 9]); - let r: u8x16 = unsafe { transmute(vaesmcq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesimcq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesimc - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let e = u8x16::from([10, 15, 8, 13, 14, 11, 12, 9, 2, 7, 0, 5, 6, 3, 4, 1]); - let r: u8x16 = unsafe { transmute(vaesimcq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1cq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1c - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x40072911, 0x40003948, 0x80000072, 0x80000003]); - let r: u32x4 = unsafe { transmute(vsha1cq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -fn test_vsha1h_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1h - let a = 8; - let e = 0x00000002; - let r = vsha1h_u32(a); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1mq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1m - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x4007a107, 0x00003d08, 0x0000007a, 0xc0000003]); - let r: u32x4 = unsafe { transmute(vsha1mq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1pq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1p - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x80062d18, 0x4000315c, 0x90000062, 0x00000003]); - let r: u32x4 = unsafe { transmute(vsha1pq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1su0q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1su0 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d]); - let r: u32x4 = unsafe { transmute(vsha1su0q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1su1q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1su1 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x0000000a, 0x0000000e, 0x0000000a, 0x00000012]); - let r: u32x4 = unsafe { transmute(vsha1su1q_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256hq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x27bb4ae0, 0xd8f61f7c, 0xb7c1ecdc, 0x10800215]); - let r: u32x4 = unsafe { transmute(vsha256hq_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256h2q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h2 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x6989ee0d, 0x4b055920, 0x52800a12, 0x00000014]); - let r: u32x4 = unsafe { transmute(vsha256h2q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256su0q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su0 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x02004000, 0x04008001, 0x0600c002, 0x08010003]); - let r: u32x4 = unsafe { transmute(vsha256su0q_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256su1q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su1 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x00044005, 0x0004e007, 0xa802211b, 0xec036145]); - let r: u32x4 = unsafe { transmute(vsha256su1q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512hq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512h - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x001c805053800005, 0x0015400002800003]); - let r: u64x2 = unsafe { transmute(vsha512hq_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512h2q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512h2 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x401000514a000405, 0x0000004108000005]); - let r: u64x2 = unsafe { transmute(vsha512h2q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512su0q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512su0 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([0x8100000000000000, 0x0200000000000002]); - let r: u64x2 = unsafe { transmute(vsha512su0q_u64(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512su1q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512su1 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x0000400000000014, 0x000060000000001e]); - let r: u64x2 = unsafe { transmute(vsha512su1q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v16i8 - let a = i8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = i8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = i8x16::from([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]); - let r: i8x16 = unsafe { transmute(veor3q_s8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v8i16 - let a = i16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = i16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = i16x8::from([24, 25, 26, 27, 28, 29, 30, 31]); - let r: i16x8 = unsafe { transmute(veor3q_s16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v4i32 - let a = i32x4::from([0, 1, 2, 3]); - let b = i32x4::from([4, 5, 6, 7]); - let c = i32x4::from([8, 9, 10, 11]); - let e = i32x4::from([12, 13, 14, 15]); - let r: i32x4 = unsafe { transmute(veor3q_s32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v2i64 - let a = i64x2::from([0, 1]); - let b = i64x2::from([2, 3]); - let c = i64x2::from([4, 5]); - let e = i64x2::from([6, 7]); - let r: i64x2 = unsafe { transmute(veor3q_s64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v16i8 - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = u8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = u8x16::from([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]); - let r: u8x16 = unsafe { transmute(veor3q_u8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v8i16 - let a = u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = u16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = u16x8::from([24, 25, 26, 27, 28, 29, 30, 31]); - let r: u16x8 = unsafe { transmute(veor3q_u16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v4i32 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([12, 13, 14, 15]); - let r: u32x4 = unsafe { transmute(veor3q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v2i64 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([6, 7]); - let r: u64x2 = unsafe { transmute(veor3q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v16i8 - let a = i8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = i8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let r: i8x16 = unsafe { transmute(vbcaxq_s8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v8i16 - let a = i16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = i16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let r: i16x8 = unsafe { transmute(vbcaxq_s16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v4i32 - let a = i32x4::from([0, 1, 2, 3]); - let b = i32x4::from([4, 5, 6, 7]); - let c = i32x4::from([8, 9, 10, 11]); - let e = i32x4::from([4, 5, 6, 7]); - let r: i32x4 = unsafe { transmute(vbcaxq_s32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v2i64 - let a = i64x2::from([0, 1]); - let b = i64x2::from([2, 3]); - let c = i64x2::from([4, 5]); - let e = i64x2::from([2, 3]); - let r: i64x2 = unsafe { transmute(vbcaxq_s64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v16i8 - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = u8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let r: u8x16 = unsafe { transmute(vbcaxq_u8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v8i16 - let a = u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = u16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let r: u16x8 = unsafe { transmute(vbcaxq_u16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v4i32 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([4, 5, 6, 7]); - let r: u32x4 = unsafe { transmute(vbcaxq_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v2i64 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([2, 3]); - let r: u64x2 = unsafe { transmute(vbcaxq_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vrax1q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.rax1 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([4, 7]); - let r: u64x2 = unsafe { transmute(vrax1q_u64(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vxarq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.xar - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([4, 4]); - let r: u64x2 = unsafe { transmute(vxarq_u64::<63>(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -fn test_vmull_p64() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull64 - let a: u64 = 3; - let b: u64 = 6; - let e: u128 = 10; - let r: u128 = vmull_p64(a, b); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vmull_p8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull.v8i16 - let a = u8x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u8x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let e = u16x8::from([0x0000, 0x0009, 0x0014, 0x001d, 0x0030, 0x0039, 0x0024, 0x002d]); - let r: u16x8 = unsafe { transmute(vmull_p8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulh_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i16 - let a = i16x4::from([1, 2, 4, 8]); - let b = i16x4::from([16384, 16384, 16384, 16384]); - let e = i16x4::from([0, 1, 2, 4]); - let r: i16x4 = unsafe { transmute(vqdmulh_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulh_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v2i32 - let a = i32x2::from([1, 2]); - let b = i32x2::from([1073741824, 1073741824]); - let e = i32x2::from([0, 1]); - let r: i32x2 = unsafe { transmute(vqdmulh_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulhq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v8i16 - let a = i16x8::from([1, 2, 4, 8, 16, 32, 64, 128]); - let b = i16x8::from([16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384]); - let e = i16x8::from([0, 1, 2, 4, 8, 16, 32, 64]); - let r: i16x8 = unsafe { transmute(vqdmulhq_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulhq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i32 - let a = i32x4::from([1, 2, 4, 8]); - let b = i32x4::from([1073741824, 1073741824, 1073741824, 1073741824]); - let e = i32x4::from([0, 1, 2, 4]); - let r: i32x4 = unsafe { transmute(vqdmulhq_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i16.v8i8 - let a = i8x8::from([1, 2, 3, 4, -5, -6, -7, -8]); - let e = i16x4::from([3, 7, -11, -15]); - let r: i16x4 = unsafe { transmute(vpaddl_s8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i32.v4i16 - let a = i16x4::from([1, 2, -3, -4]); - let e = i32x2::from([3, -7]); - let r: i32x2 = unsafe { transmute(vpaddl_s16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v1i64.v2i32 - let a = i32x2::from([1, -2]); - let e = i64x1::from([-1]); - let r: i64x1 = unsafe { transmute(vpaddl_s32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v8i16.v16i8 - let a = i8x16::from([1, 2, 3, 4, 5, 6, 7, 8, -9, -10, -11, -12, -13, -14, -15, -16]); - let e = i16x8::from([3, 7, 11, 15, -19, -23, -27, -31]); - let r: i16x8 = unsafe { transmute(vpaddlq_s8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i32.v8i16 - let a = i16x8::from([1, 2, 3, 4, -5, -6, -7, -8]); - let e = i32x4::from([3, 7, -11, -15]); - let r: i32x4 = unsafe { transmute(vpaddlq_s16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i64.v4i32 - let a = i32x4::from([1, 2, -3, -4]); - let e = i64x2::from([3, -7]); - let r: i64x2 = unsafe { transmute(vpaddlq_s32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i16.v8i8 - let a = u8x8::from([255, 254, 253, 252, 251, 250, 249, 248]); - let e = u16x4::from([509, 505, 501, 497]); - let r: u16x4 = unsafe { transmute(vpaddl_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i32.v4i16 - let a = u16x4::from([65535, 65534, 65533, 65532]); - let e = u32x2::from([131069, 131065]); - let r: u32x2 = unsafe { transmute(vpaddl_u16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v1i64.v2i32 - let a = u32x2::from([4294967295, 4294967294]); - let e = u64x1::from([8589934589]); - let r: u64x1 = unsafe { transmute(vpaddl_u32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v8i16.v16i8 - let a = u8x16::from([ - 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, - ]); - let e = u16x8::from([509, 505, 501, 497, 493, 489, 485, 481]); - let r: u16x8 = unsafe { transmute(vpaddlq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i32.v8i16 - let a = u16x8::from([65535, 65534, 65533, 65532, 65531, 65530, 65529, 65528]); - let e = u32x4::from([131069, 131065, 131061, 131057]); - let r: u32x4 = unsafe { transmute(vpaddlq_u16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i64.v4i32 - let a = u32x4::from([4294967295, 4294967294, 4294967293, 4294967292]); - let e = u64x2::from([8589934589, 8589934585]); - let r: u64x2 = unsafe { transmute(vpaddlq_u32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -fn main() { - unsafe { - test_vpmin_s8(); - test_vpmin_s16(); - test_vpmin_s32(); - test_vpmin_u8(); - test_vpmin_u16(); - test_vpmin_u32(); - test_vpmin_f32(); - test_vpmax_s8(); - test_vpmax_s16(); - test_vpmax_s32(); - test_vpmax_u8(); - test_vpmax_u16(); - test_vpmax_u32(); - test_vpmax_f32(); - - test_vpadd_s16(); - test_vpadd_s32(); - test_vpadd_s8(); - test_vpadd_u16(); - test_vpadd_u32(); - test_vpadd_u8(); - - test_vqsub_u8(); - test_vqadd_u8(); - - test_vmaxq_f32(); - test_vminq_f32(); - test_vaddvq_f32(); - test_vrndnq_f32(); - - test_crc32(); - - test_vaeseq_u8(); - test_vaesdq_u8(); - test_vaesmcq_u8(); - test_vaesimcq_u8(); - - test_vsha1cq_u32(); - test_vsha1h_u32(); - test_vsha1mq_u32(); - test_vsha1pq_u32(); - test_vsha1su0q_u32(); - test_vsha1su1q_u32(); - - test_vsha256hq_u32(); - test_vsha256h2q_u32(); - test_vsha256su0q_u32(); - test_vsha256su1q_u32(); - - if std::arch::is_aarch64_feature_detected!("sha3") { - test_vsha512hq_u64(); - test_vsha512h2q_u64(); - test_vsha512su0q_u64(); - test_vsha512su1q_u64(); - - test_veor3q_s8(); - test_veor3q_s16(); - test_veor3q_s32(); - test_veor3q_s64(); - test_veor3q_u8(); - test_veor3q_u16(); - test_veor3q_u32(); - test_veor3q_u64(); - - test_vbcaxq_s8(); - test_vbcaxq_s16(); - test_vbcaxq_s32(); - test_vbcaxq_s64(); - test_vbcaxq_u8(); - test_vbcaxq_u16(); - test_vbcaxq_u32(); - test_vbcaxq_u64(); - - test_vrax1q_u64(); - test_vxarq_u64(); - } - - test_vmull_p64(); - test_vmull_p8(); - - test_vqdmulh_s16(); - test_vqdmulh_s32(); - test_vqdmulhq_s16(); - test_vqdmulhq_s32(); - - test_vpaddl_s8(); - test_vpaddl_s16(); - test_vpaddl_s32(); - test_vpaddlq_s8(); - test_vpaddlq_s16(); - test_vpaddlq_s32(); - - test_vpaddl_u8(); - test_vpaddl_u16(); - test_vpaddl_u32(); - test_vpaddlq_u8(); - test_vpaddlq_u16(); - test_vpaddlq_u32(); - } -} - -#[cfg(not(target_arch = "aarch64"))] -fn main() {} diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index e1287683d495d..5fc5f17682cc8 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -294,6 +294,19 @@ unsafe fn test_simd() { if is_x86_feature_detected!("avx512f") { test_zmm_roundtrip(); } + + #[cfg(not(jit))] + { + if is_x86_feature_detected!("pclmulqdq") { + test_mm_clmulepi64_si128(); + } + if is_x86_feature_detected!("vpclmulqdq") { + test_mm256_clmulepi64_epi128(); + } + if is_x86_feature_detected!("vpclmulqdq") && is_x86_feature_detected!("avx512f") { + test_mm512_clmulepi64_epi128(); + } + } } } @@ -395,6 +408,14 @@ pub fn assert_eq_m256i(a: __m256i, b: __m256i) { } } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx512f")] +pub fn assert_eq_m512i(a: __m512i, b: __m512i) { + unsafe { + assert_eq!(std::mem::transmute::<_, [u64; 8]>(a), std::mem::transmute::<_, [u64; 8]>(b)) + } +} + #[cfg(target_arch = "x86_64")] #[target_feature(enable = "sse2")] unsafe fn test_mm_cvtsi128_si64() { @@ -537,6 +558,62 @@ unsafe fn test_mm256_permutevar8x32_epi32() { assert_eq_m256i(r, e); } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "pclmulqdq")] +#[cfg(not(jit))] +unsafe fn test_mm_clmulepi64_si128() { + // Constants taken from https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf + let a = _mm_set_epi64x(0x7b5b546573745665, 0x63746f725d53475d); + let b = _mm_set_epi64x(0x4869285368617929, 0x5b477565726f6e5d); + let r00 = _mm_set_epi64x(0x1d4d84c85c3440c0, 0x929633d5d36f0451u64.cast_signed()); + let r01 = _mm_set_epi64x(0x1bd17c8d556ab5a1, 0x7fa540ac2a281315); + let r10 = _mm_set_epi64x(0x1a2bf6db3a30862f, 0xbabf262df4b7d5c9u64.cast_signed()); + let r11 = _mm_set_epi64x(0x1d1e1f2c592e7c45, 0xd66ee03e410fd4edu64.cast_signed()); + + assert_eq_m128i(_mm_clmulepi64_si128::<0x00>(a, b), r00); + assert_eq_m128i(_mm_clmulepi64_si128::<0x10>(a, b), r01); + assert_eq_m128i(_mm_clmulepi64_si128::<0x01>(a, b), r10); + assert_eq_m128i(_mm_clmulepi64_si128::<0x11>(a, b), r11); + + let a0 = _mm_set_epi64x(0x0000000000000000, 0x8000000000000000u64.cast_signed()); + let r = _mm_set_epi64x(0x4000000000000000, 0x0000000000000000); + assert_eq_m128i(_mm_clmulepi64_si128::<0x00>(a0, a0), r); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq")] +#[cfg(not(jit))] +unsafe fn test_mm256_clmulepi64_epi128() { + let a = _mm256_setr_epi64x(1, 2, 3, 4); + let b = _mm256_setr_epi64x(5, 6, 7, 8); + + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x00>(a, b), _mm256_setr_epi64x(5, 0, 9, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x01>(a, b), _mm256_setr_epi64x(10, 0, 28, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x10>(a, b), _mm256_setr_epi64x(6, 0, 24, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x11>(a, b), _mm256_setr_epi64x(12, 0, 32, 0)); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq,avx512f")] +#[cfg(not(jit))] +unsafe fn test_mm512_clmulepi64_epi128() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_setr_epi64(9, 10, 11, 12, 13, 14, 15, 16); + + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x00>(a, b), + _mm512_setr_epi64(9, 0, 29, 0, 57, 0, 45, 0), + ); + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x10>(a, b), + _mm512_setr_epi64(10, 0, 20, 0, 54, 0, 112, 0), + ); + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x11>(a, b), + _mm512_setr_epi64(20, 0, 48, 0, 36, 0, 128, 0), + ); +} + #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx2")] #[cfg(not(jit))] diff --git a/compiler/rustc_codegen_cranelift/josh-sync.toml b/compiler/rustc_codegen_cranelift/josh-sync.toml index 2de9f9489cce3..d8b49ebac131d 100644 --- a/compiler/rustc_codegen_cranelift/josh-sync.toml +++ b/compiler/rustc_codegen_cranelift/josh-sync.toml @@ -1,3 +1,4 @@ repo = "rustc_codegen_cranelift" filter = ":~(history=\"keep-trivial-merges,no-splice\")[:rev(<=5e120485964f4857f1ad70f7d661fd244d087668:prefix=compiler/rustc_codegen_cranelift,<=7bd21608dfab11ea536f7be8936cc7dfac5864fb:SQUASH)]:/compiler/rustc_codegen_cranelift" filter-version = 2 +pull-mode = "Nightly" diff --git a/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch b/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch new file mode 100644 index 0000000000000..e7038699bc998 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch @@ -0,0 +1,51 @@ +From e90274ba5bc4bee03a24e8c134ea223497c3a46b Mon Sep 17 00:00:00 2001 +From: Cathal Mullan +Date: Sat, 13 Jun 2026 20:32:02 +0100 +Subject: [PATCH] Disable SVE + +Cranelift doesn't support scalable vectors. +--- + crates/core_arch/src/aarch64/mod.rs | 8 ++++---- + crates/intrinsic-test/src/arm/mod.rs | 1 + + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/crates/core_arch/src/aarch64/mod.rs b/crates/core_arch/src/aarch64/mod.rs +index 34fa5c8..a870e31 100644 +--- a/crates/core_arch/src/aarch64/mod.rs ++++ b/crates/core_arch/src/aarch64/mod.rs +@@ -19,16 +19,16 @@ mod neon; + pub use self::neon::*; + + // The rest of `core_arch::aarch64` is available on `arm64ec` but SVE is not supported on `arm64ec`. +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + mod sve; +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] + pub use self::sve::*; + + // The rest of `core_arch::aarch64` is available on `arm64ec` but SVE is not supported on `arm64ec`. +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + mod sve2; +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] + pub use self::sve2::*; + +diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs +index 9c2d0f3..3825f41 100644 +--- a/crates/intrinsic-test/src/arm/mod.rs ++++ b/crates/intrinsic-test/src/arm/mod.rs +@@ -66,6 +66,7 @@ impl SupportedArchitecture for Arm { + + let intrinsics = intrinsics + .into_iter() ++ .filter(|i| i.extension != "SVE" && i.extension != "SVE2") + // Skip intrinsics that don't return a value. + .filter(|i| i.results.kind() != TypeKind::Void) + // Skip bfloat intrinsics - not currently supported +-- +2.54.0 + diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index d7e313a7a33ed..a6468283e1270 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-11" +channel = "nightly-2026-09-30" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index e26be307328f0..8bd41f98ce550 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -2,75 +2,34 @@ set -e -TOOLCHAIN=${TOOLCHAIN:-$(date +%Y-%m-%d)} - -function check_git_fixed_subtree() { - if [[ ! -e ./git-fixed-subtree.sh ]]; then - echo "Missing git-fixed-subtree.sh. Please run the following commands to download it:" - echo "curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o git-fixed-subtree.sh" - echo "chmod u+x git-fixed-subtree.sh" - exit 1 - fi - if [[ ! -x ./git-fixed-subtree.sh ]]; then - echo "git-fixed-subtree.sh is not executable. Please run the following command to make it executable:" - echo "chmod u+x git-fixed-subtree.sh" - exit 1 - fi -} +export GIT_CONFIG_GLOBAL=$(pwd)/../rust/josh.gitconfig +export RUSTC_GIT=../rust case $1 in "prepare") - echo "=> Installing new nightly" - rustup toolchain install --profile minimal "nightly-${TOOLCHAIN}" # Sanity check to see if the nightly exists - sed -i "s/\"nightly-.*\"/\"nightly-${TOOLCHAIN}\"/" rust-toolchain.toml - echo "=> Uninstalling all old nightlies" - for nightly in $(rustup toolchain list | grep nightly | grep -v "$TOOLCHAIN" | grep -v nightly-x86_64); do + for nightly in $(RUSTUP_AUTO_INSTALL=0 rustup toolchain list | grep nightly | grep -v nightly-x86_64); do rustup toolchain uninstall "$nightly" done + echo "=> Installing new nightly" + rustup toolchain install + ./clean_all.sh ./y.sh prepare ;; - "commit") - git add rust-toolchain.toml - git commit -m "Rustup to $(rustc -V)" - ;; "push") - check_git_fixed_subtree - - cg_clif=$(pwd) - pushd ../rust - git pull origin main + username=${2:-bjorn3} branch=sync_cg_clif-$(date +%Y-%m-%d) - git checkout -b "$branch" - "$cg_clif/git-fixed-subtree.sh" pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/rust-lang/rustc_codegen_cranelift.git main - git push -u my "$branch" - - # immediately merge the merge commit into cg_clif to prevent merge conflicts when syncing - # from rust-lang/rust later - "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust - popd - git merge sync_from_rust + rustc-josh-sync push "$branch" "$username" ;; "pull") - check_git_fixed_subtree - - RUST_VERS=$(curl "https://static.rust-lang.org/dist/$TOOLCHAIN/channel-rust-nightly-git-commit-hash.txt") - echo "Pulling $RUST_VERS ($TOOLCHAIN)" - - cg_clif=$(pwd) - pushd ../rust - git fetch origin main - git -c advice.detachedHead=false checkout "$RUST_VERS" - "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust - popd - git merge sync_from_rust -m "Sync from rust $RUST_VERS" - git branch -d sync_from_rust + git checkout -b sync_from_rust + rustc-josh-sync pull ;; *) echo "Unknown command '$1'" - echo "Usage: ./rustup.sh prepare|commit" + echo "Usage: ./rustup.sh prepare|pull|push [fork]" ;; esac diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 5a1374fe56e09..648f68588e04b 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -164,6 +164,7 @@ rm -r tests/run-make/const-trait-stable-toolchain # same rm -r tests/run-make/print-request-help-stable-unstable # same rm -r tests/run-make/issue-149402-suggest-unresolve # same rm -r tests/run-make/const-destruct-stable-toolchain # same +rm -r tests/run-make/unstable-target-feature # same rm -r tests/run-make/incr-add-rust-src-component rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path rm -r tests/run-make/export # something about rustc version mismatches diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index ca9157daae584..8e339167a5b12 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -135,8 +135,8 @@ builtin_functions! { // float intrinsics fn __powisf2(a: f32, b: i32) -> f32; fn __powidf2(a: f64, b: i32) -> f64; - // FIXME(f16_f128): `compiler-builtins` doesn't currently support `__powitf2` on MSVC. - // fn __powitf2(a: f128, b: i32) -> f128; + #[cfg(not(all(target_os = "windows", target_env = "gnu")))] + fn __powitf2(a: f128, b: i32) -> f128; fn powf(a: f32, b: f32) -> f32; fn pow(a: f64, b: f64) -> f64; fn expf(f: f32) -> f32; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 136e98964bc35..f3763b5895eba 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -855,42 +855,59 @@ pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>( ); } - "llvm.x86.pclmulqdq" => { + "llvm.x86.pclmulqdq" | "llvm.x86.pclmulqdq.256" | "llvm.x86.pclmulqdq.512" => { // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128&ig_expand=772 + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_clmulepi64_epi128&ig_expand=752 + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_clmulepi64_epi128&ig_expand=753 intrinsic_args!(fx, args => (a, b, _imm8); intrinsic); - let a = a.load_scalar(fx); - let b = b.load_scalar(fx); + let (lane_count, intrinsic_name) = match intrinsic { + "llvm.x86.pclmulqdq" => (1, "_mm_clmulepi64_si128"), + "llvm.x86.pclmulqdq.256" => (2, "_mm256_clmulepi64_epi128"), + "llvm.x86.pclmulqdq.512" => (4, "_mm512_clmulepi64_epi128"), + _ => unreachable!(), + }; - let imm8 = - if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) { - imm8 - } else { - fx.tcx.dcx().span_fatal( - span, - "Index argument for `_mm_clmulepi64_si128` is not a constant", - ); - }; + let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) else { + fx.tcx.dcx().span_fatal( + span, + format!("Index argument for `{intrinsic_name}` is not a constant"), + ) + }; let imm8 = imm8.to_u8(); - codegen_inline_asm_inner( - fx, - &[InlineAsmTemplatePiece::String(format!("pclmulqdq xmm0, xmm1, {imm8}").into())], - &[ - CInlineAsmOperand::InOut { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), - _late: true, - in_value: a, - out_place: Some(ret), - }, - CInlineAsmOperand::In { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), - value: b, - }, - ], - InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, - ); + // Cranelift does not currently support 256-bit or 512-bit SSA values, so we use the + // 128-bit instruction on each 128-bit lane. + for lane in 0..lane_count { + let a = a.value_typed_lane(fx, fx.tcx.types.u128, lane).load_scalar(fx); + let b = b.value_typed_lane(fx, fx.tcx.types.u128, lane).load_scalar(fx); + let ret_lane = ret.place_typed_lane(fx, fx.tcx.types.u128, lane); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + format!("pclmulqdq xmm0, xmm1, {imm8}").into(), + )], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86( + X86InlineAsmReg::xmm0, + )), + _late: true, + in_value: a, + out_place: Some(ret_lane), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86( + X86InlineAsmReg::xmm1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } } "llvm.x86.aesni.aeskeygenassist" => { diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 07214096b3e49..acf5c75e4d8cd 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -55,19 +55,14 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => GlobalKind::Exported, + | Linkage::ExternalWeak => GlobalKind::Exported, } } /// The attribute a global *definition* needs on top of its [`GlobalKind`] to get this linkage. #[cfg(feature = "master")] pub fn global_linkage_attribute<'gcc>(linkage: Linkage) -> Option> { - match linkage { - Linkage::Common => Some(VarAttribute::Common), - _ if linkage_needs_weak_attribute(linkage) => Some(VarAttribute::Weak), - _ => None, - } + if linkage_needs_weak_attribute(linkage) { Some(VarAttribute::Weak) } else { None } } /// The type of a function *definition* with an explicit `#[linkage]`. @@ -85,17 +80,12 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => FunctionType::Exported, + | Linkage::ExternalWeak => FunctionType::Exported, } } /// Whether a definition with this linkage must carry the `weak` attribute, so that a strong /// definition in another object file wins over it instead of clashing with it. -/// -/// `common` is in here for functions only: GCC honours that attribute on a variable, but drops it -/// on a function, so a common function falls back to weak. Globals go through -/// `global_linkage_attribute` instead. #[cfg(feature = "master")] pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { match linkage { @@ -103,8 +93,7 @@ pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => true, + | Linkage::ExternalWeak => true, Linkage::External | Linkage::AvailableExternally | Linkage::Internal => false, } } diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index c58ab6d2a9bef..0784064547e0f 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -13,8 +13,7 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_log::tracing::trace; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - self, Allocation, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, - read_target_uint, + self, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, read_target_uint, }; use rustc_middle::mono::MonoItem; use rustc_middle::ty::layout::LayoutOf; @@ -113,12 +112,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); - // A common symbol is storage the linker allocates and zero-fills, so giving the definition - // an initializer — even an all-zero one — takes it back out of `.comm`. A non-zero one is - // kept: the symbol is then an ordinary definition, which is what GCC does with it too. - if attrs.linkage != Some(Linkage::Common) || !is_zero_initializer(alloc) { - global.global_set_initializer_rvalue(value); - } + global.global_set_initializer_rvalue(value); // As an optimization, all shared statics which do not have interior // mutability are placed into read-only memory. @@ -458,17 +452,6 @@ pub(crate) fn const_alloc_to_gcc_uncached<'gcc>( cx.const_struct(&llvals, true) } -/// Whether this allocation is all zeroes, and so needs no initializer to be spelled out. -fn is_zero_initializer(alloc: &Allocation) -> bool { - alloc.provenance().ptrs().is_empty() - // This `inspect` is okay: it is within the bounds of the allocation, there is no provenance - // to misread, and it does not affect interpreter execution. - && alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.size().bytes_usize()) - .iter() - .all(|&byte| byte == 0) -} - fn codegen_static_initializer<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId, diff --git a/compiler/rustc_codegen_gcc/tests/c/import_linkage.c b/compiler/rustc_codegen_gcc/tests/c/import_linkage.c index f2beb9603d08b..227fa1c848ddf 100644 --- a/compiler/rustc_codegen_gcc/tests/c/import_linkage.c +++ b/compiler/rustc_codegen_gcc/tests/c/import_linkage.c @@ -6,12 +6,9 @@ #include -int32_t external_value = 1; int32_t available_externally_value = 2; int32_t linkonce_value = 3; int32_t linkonce_odr_value = 4; int32_t weak_value = 5; int32_t weak_odr_value = 6; -int32_t common_value = 7; int32_t extern_weak_value = 8; -int32_t internal_value = 9; diff --git a/compiler/rustc_codegen_gcc/tests/c/static_linkage.c b/compiler/rustc_codegen_gcc/tests/c/static_linkage.c index 787e61f9cf105..00360d8633e68 100644 --- a/compiler/rustc_codegen_gcc/tests/c/static_linkage.c +++ b/compiler/rustc_codegen_gcc/tests/c/static_linkage.c @@ -1,9 +1,6 @@ /* Strong definitions of the statics that `tests/run/static_linkage.rs` also defines, but weakly. * The linker has to keep these and drop the Rust ones; a backend that emits the Rust definitions - * as ordinary global symbols fails the link with a duplicate definition instead. - * - * `internal_static` is the opposite case: the Rust side keeps its own, and the two definitions - * coexist because the Rust one is local. */ + * as ordinary global symbols fails the link with a duplicate definition instead. */ #include @@ -11,8 +8,6 @@ int32_t weak_static = 1; int32_t weak_odr_static = 2; int32_t linkonce_static = 3; int32_t linkonce_odr_static = 4; -int32_t common_static = 5; -int32_t internal_static = 200; /* `available_externally` promises the real definition lives elsewhere: a backend may read this one * or emit an equivalent copy of the Rust initializer, so the two have to hold the same value. */ @@ -29,9 +24,5 @@ int32_t c_read_all(void) return 13; if (linkonce_odr_static != 4) return 14; - if (common_static != 5) - return 15; - if (internal_static != 200) - return 16; return 0; } diff --git a/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs b/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs index bf5cb9e532799..58008ecbc9746 100644 --- a/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs +++ b/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs @@ -19,8 +19,6 @@ extern crate mini_core; use mini_core::*; extern "C" { - #[linkage = "external"] - static external_value: *const i32; #[linkage = "available_externally"] static available_externally_value: *const i32; #[linkage = "linkonce"] @@ -31,15 +29,8 @@ extern "C" { static weak_value: *const i32; #[linkage = "weak_odr"] static weak_odr_value: *const i32; - #[linkage = "common"] - static common_value: *const i32; #[linkage = "extern_weak"] static extern_weak_value: *const i32; - // An import is an undefined reference whatever the flavour says. Upstream bug: rustc lowers - // this one to an internal declaration, which LLVM's verifier rejects ("Global is external, but - // doesn't have external or weak linkage!") and which crashes cg_llvm at -O3. - #[linkage = "internal"] - static internal_value: *const i32; // Nothing defines this one, so it stays null instead of breaking the link. #[linkage = "extern_weak"] @@ -49,9 +40,6 @@ extern "C" { #[no_mangle] extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { unsafe { - if *external_value != 1 { - return 1; - } if *available_externally_value != 2 { return 2; } @@ -67,15 +55,9 @@ extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { if *weak_odr_value != 6 { return 6; } - if *common_value != 7 { - return 7; - } if *extern_weak_value != 8 { return 8; } - if *internal_value != 9 { - return 9; - } if undefined_value as usize != 0 { return 10; } diff --git a/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs b/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs index 7b911c064d797..3c9303fa8cf16 100644 --- a/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs +++ b/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs @@ -7,8 +7,7 @@ // `tests/c/static_linkage.c`, which defines the overridable ones strongly. // // If `predefine_static` were to ignore its `linkage` argument outright, every static would come out as -// an ordinary global symbol: the overridable ones would clash with the C definitions at link time, and -// `internal` would export a symbol it should have kept private. +// an ordinary global symbol: the overridable ones would clash with the C definitions at link time. #![feature(linkage, no_core)] #![no_std] @@ -34,16 +33,6 @@ pub static linkonce_static: i32 = 0; #[no_mangle] pub static linkonce_odr_static: i32 = 0; -// `common` is only valid on a mutable global: LLVM rejects a constant one. -#[linkage = "common"] -#[no_mangle] -pub static mut common_static: i32 = 0; - -// Private to this crate, so the C definition of the same name is a different object. -#[linkage = "internal"] -#[no_mangle] -pub static internal_static: i32 = 100; - // Not overridden by the C side: the definition here is the one that survives. #[linkage = "weak"] #[no_mangle] @@ -66,9 +55,6 @@ extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { return result; } - if internal_static != 100 { - return 1; - } if only_weak_static != 6 { return 2; } diff --git a/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs b/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs index 677f01353401a..406b85df10b02 100644 --- a/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs +++ b/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs @@ -41,9 +41,6 @@ extern "C" fn linkonce_odr_function() -> i32 { 4 } -// `#[linkage = "common"]` is absent on purpose: a common symbol is `SHN_COMMON`, which the object -// format only allows for objects, so no backend can give a function that linkage. - // Not overridden by the C side: the definition here is the one that runs. #[linkage = "weak"] #[no_mangle] diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index cf278694399f3..77cb37edec770 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -237,7 +237,6 @@ pub(crate) fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, Linkage::Internal => llvm::Linkage::InternalLinkage, Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, - Linkage::Common => llvm::Linkage::CommonLinkage, } } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 2fa6f3fc06550..5433b9da6f2b6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -228,7 +228,6 @@ fn prefix_and_suffix<'tcx>( // LTO can fail when internal linkage is used. emit_fatal("naked functions may not have internal linkage") } - Linkage::Common => emit_fatal("Functions may not have common linkage"), Linkage::AvailableExternally => { // this would make the function equal an extern definition emit_fatal("Functions may not have available_externally linkage") diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs index cad775cc98641..76b2c428ec84d 100644 --- a/compiler/rustc_data_structures/src/fx.rs +++ b/compiler/rustc_data_structures/src/fx.rs @@ -28,7 +28,7 @@ macro_rules! define_stable_id_collections { } pub mod default { - use super::{FxBuildHasher, FxHashMap, FxHashSet}; + use super::{FxBuildHasher, FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; // FIXME: These two functions will become unnecessary after // lands and we start using the corresponding @@ -40,4 +40,12 @@ pub mod default { pub const fn fx_hash_set() -> FxHashSet { FxHashSet::with_hasher(FxBuildHasher) } + + pub const fn fx_index_map() -> FxIndexMap { + FxIndexMap::with_hasher(FxBuildHasher) + } + + pub const fn fx_index_set() -> FxIndexSet { + FxIndexSet::with_hasher(FxBuildHasher) + } } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index efed5994f63fe..e7f20766bc8eb 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -115,7 +115,11 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran Err(CopyImplementationError::HasDestructor(did)) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; let impl_ = tcx.def_span(did); - Err(tcx.dcx().emit_err(diagnostics::CopyImplOnTypeWithDtor { span, impl_ })) + Err(tcx.dcx().emit_err(diagnostics::TraitImplOnTypeWithDtor { + span, + impl_, + trait_name: sym::Copy, + })) } Err(CopyImplementationError::HasUnsafeFields) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; @@ -537,6 +541,15 @@ pub(crate) fn reborrow_info<'tcx>( assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field.ty, field.span)?; } + if let Some(did) = def.destructor(tcx).map(|dtor| dtor.did) { + let impl_ = tcx.def_span(did); + return Err(tcx.dcx().emit_err(diagnostics::TraitImplOnTypeWithDtor { + span, + impl_, + trait_name: sym::Reborrow, + })); + } + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 421f0b52d5a3b..7e5e794b75f78 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -296,13 +296,14 @@ pub(crate) struct FieldAlreadyDeclaredNestedHelp { } #[derive(Diagnostic)] -#[diag("the trait `Copy` cannot be implemented for this type; the type has a destructor", code = E0184)] -pub(crate) struct CopyImplOnTypeWithDtor { +#[diag("the trait `{$trait_name}` cannot be implemented for this type; the type has a destructor", code = E0184)] +pub(crate) struct TraitImplOnTypeWithDtor { #[primary_span] - #[label("`Copy` not allowed on types with destructors")] + #[label("`{$trait_name}` not allowed on types with destructors")] pub span: Span, #[note("destructor declared here")] pub impl_: Span, + pub trait_name: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 02e6b8188a876..14cb73e28c871 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -39,7 +39,7 @@ use rustc_lint_defs::builtin::{TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::cast::{CastKind, CastTy}; +use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, Unnormalized, VariantDef, elaborate, @@ -52,6 +52,22 @@ use tracing::{debug, instrument}; use super::FnCtxt; use crate::{diagnostics, type_error_struct}; +/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) +/// (or rustc_hir_typeck/src/cast.rs). +#[derive(Copy, Clone, Debug)] +pub enum CastKind { + PtrPtrCast, + PtrAddrCast, + AddrPtrCast, + NumericCast, + EnumCast, + PrimIntCast, + U8CharCast, + ArrayPtrCast, + FnPtrPtrCast, + FnPtrAddrCast, +} + /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] @@ -912,7 +928,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// directly. coercion-cast is handled in check instead of here. fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result> { use rustc_middle::ty::cast::CastTy::*; - use rustc_middle::ty::cast::IntTy::*; let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) { @@ -947,7 +962,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // a cast. ty::Ref(_, inner_ty, mutbl) => { return match t_cast { - Int(_) | Float => match *inner_ty.kind() { + Int | CEnum | Bool | Char | Float => match *inner_ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) @@ -979,19 +994,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { } match (t_from, t_cast) { // These types have invariants! can't cast into them. - (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), + (_, CEnum | FnPtr) => Err(CastError::NonScalar), // * -> Bool - (_, Int(Bool)) => Err(CastError::CastToBool), + (_, Bool) => Err(CastError::CastToBool), // * -> Char - (Int(U(ty::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast - (_, Int(Char)) => Err(CastError::CastToChar), + (Int, Char) if self.expr_ty == fcx.tcx.types.u8 => { + Ok(CastKind::U8CharCast) // u8-char-cast + } + (_, Char) => Err(CastError::CastToChar), // prim -> float,ptr - (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), + (Bool | CEnum | Char, Float) => Err(CastError::NeedViaInt), - (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { + (Bool | CEnum | Char | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { Err(CastError::IllegalCast) } @@ -999,24 +1016,24 @@ 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(_)) => self.check_ptr_addr_cast(fcx, m_expr), + (Ptr(m_expr), Int) => self.check_ptr_addr_cast(fcx, m_expr), - (FnPtr, Int(_)) => { + (FnPtr, Int) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast - (Int(_), Ptr(mt)) => 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), // enum -> int - (Int(CEnum), Int(_)) => self.check_enum_cast(fcx), + (CEnum, Int) => self.check_enum_cast(fcx), // prim -> prim - (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + (Char | Bool, Int) => Ok(CastKind::PrimIntCast), - (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + (Int | Float, Int | Float) => Ok(CastKind::NumericCast), } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 7dd920c7e39bd..5348ee0ccaac1 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1544,10 +1544,12 @@ impl CrateMetadata { fn get_proc_macro_quoted_span(&self, tcx: TyCtxt<'_>, index: usize) -> Span { self.root - .tables + .proc_macro_data + .as_ref() + .expect("missing proc macro data") .proc_macro_quoted_spans .get(self, index) - .unwrap_or_else(|| panic!("Missing proc macro quoted span: {index:?}")) + .unwrap_or_else(|| panic!("missing proc macro quoted span: {index:?}")) .decode((self, tcx)) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 40b61a52d2310..03493d0e1b6a9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -404,35 +404,42 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. -macro_rules! record { +// Also those macros greatly simplify refactorings and modifications of writing to tables. +macro_rules! record_some_lazy { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ - { - let value = $value; - let lazy = $self.lazy(value); - $self.$tables.$table.set_some($def_id.index, lazy); - } + let value = $value; + let lazy = $self.lazy(value); + $self.$tables.$table.set_some($def_id.index, lazy); }}; } +macro_rules! record_some { + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => { + $self.$tables.$table.set_some($def_id.index, $value) + }; +} + +macro_rules! record_value { + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => { + $self.$tables.$table.set($def_id.index, $value) + }; +} + // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_array($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. macro_rules! record_array { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ - { - let value = $value; - let lazy = $self.lazy_array(value); - $self.$tables.$table.set_some($def_id.index, lazy); - } + let value = $value; + let lazy = $self.lazy_array(value); + $self.$tables.$table.set_some($def_id.index, lazy); }}; } macro_rules! record_defaulted_array { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ - { - let value = $value; - let lazy = $self.lazy_array(value); - $self.$tables.$table.set($def_id.index, lazy); - } + let value = $value; + let lazy = $self.lazy_array(value); + $self.$tables.$table.set($def_id.index, lazy); }}; } @@ -527,18 +534,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for def_id in std::iter::once(CRATE_DEF_ID) .chain(self.tcx.resolutions(()).proc_macros.iter().copied()) { - let def_key = self.lazy(defs.def_key(def_id)); + let def_key = defs.def_key(def_id); let def_path_hash = defs.def_path_hash(def_id); - self.tables.def_keys.set_some(def_id.local_def_index, def_key); - self.tables - .def_path_hashes - .set(def_id.local_def_index, def_path_hash.local_hash().as_u64()); + let def_id = def_id.to_def_id(); + + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_value!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } else { for (def_index, def_key, def_path_hash) in defs.enumerated_keys_and_path_hashes() { - let def_key = self.lazy(def_key); - self.tables.def_keys.set_some(def_index, def_key); - self.tables.def_path_hashes.set(def_index, def_path_hash.local_hash().as_u64()); + let def_id = LocalDefId { local_def_index: def_index }.to_def_id(); + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_value!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } } @@ -1444,7 +1451,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if state.is_doc_hidden { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } - self.tables.attr_flags.set(def_id.local_def_index, attr_flags); + + record_value!(self.tables.attr_flags[def_id.to_def_id()] <- attr_flags) } fn encode_def_ids(&mut self) { @@ -1461,7 +1469,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for local_id in tcx.iter_local_def_id() { let def_id = local_id.to_def_id(); let def_kind = tcx.def_kind(local_id); - self.tables.def_kind.set_some(def_id.index, def_kind); + record_some!(self.tables.def_kind[def_id] <- def_kind); // The `DefCollector` will sometimes create unnecessary `DefId`s // for trivial const arguments which are directly lowered to @@ -1482,33 +1490,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) && let Some(anon) = field.default { - record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + record_some_lazy!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); } if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); - record!(self.tables.def_span[def_id] <- def_span); + record_some_lazy!(self.tables.def_span[def_id] <- def_span); } if should_encode_attrs(def_kind) { self.encode_attrs(local_id); } if should_encode_expn_that_defined(def_kind) { - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); + record_some_lazy!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); } if should_encode_span(def_kind) && let Some(ident_span) = tcx.def_ident_span(def_id) { - record!(self.tables.def_ident_span[def_id] <- ident_span); + record_some_lazy!(self.tables.def_ident_span[def_id] <- ident_span); } if def_kind.has_codegen_attrs() { - record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); + record_some_lazy!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); } if should_encode_visibility(def_kind) { let vis = self .tcx .local_visibility(local_id) .map_id(|mod_id| mod_id.to_local_def_id().local_def_index); - record!(self.tables.visibility[def_id] <- vis); + record_some_lazy!(self.tables.visibility[def_id] <- vis); } if should_encode_stability(def_kind) { self.encode_stability(def_id); @@ -1521,46 +1529,46 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_array!(self.tables.variances_of[def_id] <- v); } if should_encode_fn_sig(def_kind) { - record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record_some_lazy!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } if should_encode_generics(def_kind) { let g = tcx.generics_of(def_id); - record!(self.tables.generics_of[def_id] <- g); - record!(self.tables.explicit_clauses_of[def_id] <- self.tcx.explicit_clauses_of(def_id)); + record_some_lazy!(self.tables.generics_of[def_id] <- g); + record_some_lazy!(self.tables.explicit_clauses_of[def_id] <- self.tcx.explicit_clauses_of(def_id)); let inferred_outlives = self.tcx.inferred_outlives_of(def_id); record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives); for param in &g.own_params { if let ty::GenericParamDefKind::Const { has_default: true, .. } = param.kind { let default = self.tcx.const_param_default(param.def_id); - record!(self.tables.const_param_default[param.def_id] <- default); + record_some_lazy!(self.tables.const_param_default[param.def_id] <- default); } } } if tcx.is_conditionally_const(def_id) { - record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); + record_some_lazy!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); } if should_encode_type(tcx, local_id, def_kind) { - record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); + record_some_lazy!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); } if should_encode_constness(def_kind) { let constness = self.tcx.constness(def_id); - self.tables.constness.set(def_id.index, constness); + record_value!(self.tables.constness[def_id] <- constness) } if let DefKind::Fn | DefKind::AssocFn = def_kind { let asyncness = tcx.asyncness(def_id); - self.tables.asyncness.set(def_id.index, asyncness); + record_value!(self.tables.asyncness[def_id] <- asyncness); record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id)); } if let Some(name) = tcx.intrinsic(def_id) { - record!(self.tables.intrinsic[def_id] <- name); + record_some_lazy!(self.tables.intrinsic[def_id] <- name); } if let DefKind::TyParam | DefKind::Trait = def_kind { let default = self.tcx.object_lifetime_default(def_id); - record!(self.tables.object_lifetime_default[def_id] <- default); + record_some_lazy!(self.tables.object_lifetime_default[def_id] <- default); } if let DefKind::Trait = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record_some_lazy!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record_defaulted_array!(self.tables.explicit_super_clauses_of[def_id] <- self.tcx.explicit_super_clauses_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_clauses_of[def_id] <- @@ -1574,7 +1582,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } if let DefKind::TraitAlias = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record_some_lazy!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record_defaulted_array!(self.tables.explicit_super_clauses_of[def_id] <- self.tcx.explicit_super_clauses_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_clauses_of[def_id] <- @@ -1595,28 +1603,28 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { - self.tables.coroutine_kind.set(def_id.index, Some(coroutine_kind)) + record_value!(self.tables.coroutine_kind[def_id] <- Some(coroutine_kind)) } if def_kind == DefKind::Closure && tcx.type_of(def_id).skip_binder().is_coroutine_closure() { let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id); - self.tables - .coroutine_for_closure - .set_some(def_id.index, coroutine_for_closure.into()); + record_some!(self.tables.coroutine_for_closure[def_id] <- coroutine_for_closure.into()); // If this async closure has a by-move body, record it too. if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) { - self.tables.coroutine_by_move_body_def_id.set_some( - coroutine_for_closure.index, - self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into(), - ); + let id = self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure); + record_some!( + self.tables.coroutine_by_move_body_def_id[coroutine_for_closure] <- id.into() + ) } } if let DefKind::Static { .. } = def_kind { if !self.tcx.is_foreign_item(def_id) { match self.tcx.eval_static_initializer(def_id) { - Ok(data) => record!(self.tables.eval_static_initializer[def_id] <- data), + Ok(data) => { + record_some_lazy!(self.tables.eval_static_initializer[def_id] <- data) + } Err(err) => match err { interpret::ErrorHandled::Reported(_, _) => { self.tcx.dcx().delayed_bug(format!( @@ -1640,50 +1648,49 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_info_for_macro(local_id); } if let DefKind::TyAlias = def_kind { - self.tables - .type_alias_is_checked - .set(def_id.index, self.tcx.type_alias_is_checked(def_id)); + record_value!(self.tables.type_alias_is_checked[def_id] <- self.tcx.type_alias_is_checked(def_id)); + if self.tcx.type_alias_is_checked(def_id) { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_self_bounds(def_id); - record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); + record_some_lazy!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } if let DefKind::AssocTy = def_kind { let assoc_item = tcx.associated_item(def_id); match assoc_item.container { ty::AssocContainer::Trait => { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } ty::AssocContainer::InherentImpl => { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } ty::AssocContainer::TraitImpl(_) => {} } } if let DefKind::AnonConst = def_kind { - record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); + record_some_lazy!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if let DefKind::Const | DefKind::AssocConst = def_kind { - record!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); + record_some_lazy!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); + record_some_lazy!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); - record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); + record_some_lazy!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); } } @@ -1695,7 +1702,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions { - record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); + record_some_lazy!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); } for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope { @@ -1722,10 +1729,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let def_id = local_def_id.to_def_id(); let tcx = self.tcx; let adt_def = tcx.adt_def(def_id); - record!(self.tables.repr_options[def_id] <- adt_def.repr()); + record_some_lazy!(self.tables.repr_options[def_id] <- adt_def.repr()); let params_in_repr = self.tcx.params_in_repr(def_id); - record!(self.tables.params_in_repr[def_id] <- params_in_repr); + record_some_lazy!(self.tables.params_in_repr[def_id] <- params_in_repr); if adt_def.is_enum() { let module_children = tcx.module_children_local(local_def_id); @@ -1745,7 +1752,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)), is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - record!(self.tables.variant_data[variant.def_id] <- data); + record_some_lazy!(self.tables.variant_data[variant.def_id] <- data); record_array!(self.tables.associated_item_or_field_def_ids[variant.def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); @@ -1753,8 +1760,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { })); for field in &variant.fields { - self.tables.safety.set(field.did.index, field.safety); - record!( + record_value!(self.tables.safety[field.did] <- field.safety); + record_some_lazy!( self.tables.mut_restriction[field.did] <- field.mut_restriction ); } @@ -1762,16 +1769,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { let fn_sig = tcx.fn_sig(ctor_def_id); // FIXME only encode signature for ctor_def_id - record!(self.tables.fn_sig[variant.def_id] <- fn_sig); + record_some_lazy!(self.tables.fn_sig[variant.def_id] <- fn_sig); } } if let Some(destructor) = tcx.adt_destructor(local_def_id) { - record!(self.tables.adt_destructor[def_id] <- destructor); + record_some_lazy!(self.tables.adt_destructor[def_id] <- destructor); } if let Some(destructor) = tcx.adt_async_destructor(local_def_id) { - record!(self.tables.adt_async_destructor[def_id] <- destructor); + record_some_lazy!(self.tables.adt_async_destructor[def_id] <- destructor); } } @@ -1787,7 +1794,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // items - we encode information about proc-macros later on. if self.is_proc_macro { // Encode this here because we don't do it in encode_def_ids. - record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); + record_some_lazy!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { let module_children = tcx.module_children_local(local_def_id); @@ -1826,10 +1833,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let item = tcx.associated_item(def_id); if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { - self.tables.defaultness.set(def_id.index, item.defaultness(tcx)); + record_value!(self.tables.defaultness[def_id] <- item.defaultness(tcx)); } - record!(self.tables.assoc_container[def_id] <- item.container); + record_some_lazy!(self.tables.assoc_container[def_id] <- item.container); if let AssocContainer::Trait = item.container && item.is_type() @@ -1842,7 +1849,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { - record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); + record_some_lazy!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) { record_array!( self.tables.assumed_wf_types_for_rpitit[def_id] @@ -1878,53 +1885,53 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if encode_opt { - record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); - self.tables - .cross_crate_inlinable - .set(def_id.to_def_id().index, self.tcx.cross_crate_inlinable(def_id)); - record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] + record_some_lazy!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); + + record_value!(self.tables.cross_crate_inlinable[def_id.to_def_id()] <- self.tcx.cross_crate_inlinable(def_id)); + + record_some_lazy!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] <- tcx.closure_saved_names_of_captured_variables(def_id)); if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record_some_lazy!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } } let mut is_trivial = false; if encode_const { if let Some((val, ty)) = tcx.trivial_const(def_id) { is_trivial = true; - record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); + record_some_lazy!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); } else { is_trivial = false; - record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); + record_some_lazy!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); } // FIXME(generic_const_exprs): this feels wrong to have in `encode_mir` let abstract_const = tcx.thir_abstract_const(def_id); if let Ok(Some(abstract_const)) = abstract_const { - record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); + record_some_lazy!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); } if should_encode_const(tcx.def_kind(def_id)) { let qualifs = tcx.mir_const_qualif(def_id); - record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); + record_some_lazy!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); let body = tcx.hir_maybe_body_owned_by(def_id); if let Some(body) = body { let const_data = rendered_const(self.tcx, &body, def_id); - record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); + record_some_lazy!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); } } } if !is_trivial { - record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); + record_some_lazy!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); } if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record_some_lazy!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } } @@ -1950,7 +1957,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_stability(def_id) { - record!(self.tables.lookup_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_stability[def_id] <- stab) } } } @@ -1961,7 +1968,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_const_stability(def_id) { - record!(self.tables.lookup_const_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_const_stability[def_id] <- stab) } } } @@ -1972,7 +1979,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) { - record!(self.tables.lookup_default_body_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_default_body_stability[def_id] <- stab) } } } @@ -1980,7 +1987,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn encode_deprecation(&mut self, def_id: DefId) { if let Some(depr) = self.tcx.lookup_deprecation(def_id) { - record!(self.tables.lookup_deprecation_entry[def_id] <- depr); + record_some_lazy!(self.tables.lookup_deprecation_entry[def_id] <- depr); } } @@ -1989,8 +1996,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let (_, macro_def, _) = tcx.hir_expect_item(def_id).expect_macro(); - self.tables.is_macro_rules.set(def_id.local_def_index, macro_def.macro_rules); - record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); + record_value!(self.tables.is_macro_rules[def_id.to_def_id()] <- macro_def.macro_rules); + record_some_lazy!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); } fn encode_native_libraries(&mut self) -> LazyArray { @@ -2039,24 +2046,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index; let stability = tcx.lookup_stability(CRATE_DEF_ID); - for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { - let span = self.lazy(span); - self.tables.proc_macro_quoted_spans.set_some(i, span); - } - self.tables.def_kind.set_some(LOCAL_CRATE.as_def_id().index, DefKind::Mod); - record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); + record_some!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); + record_some_lazy!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local()); let vis = tcx .local_visibility(CRATE_DEF_ID) .map_id(|mod_id| mod_id.to_local_def_id().local_def_index); - record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); + record_some_lazy!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); if let Some(stability) = stability { - record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); + record_some_lazy!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); } self.encode_deprecation(LOCAL_CRATE.as_def_id()); if let Some(res_map) = tcx.resolutions(()).doc_link_resolutions.get(&CRATE_MOD_ID) { - record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); + record_some_lazy!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); } if let Some(traits) = tcx.resolutions(()).doc_link_traits_in_scope.get(&CRATE_MOD_ID) { record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits); @@ -2103,20 +2106,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind.into())); + record_some!(self.tables.def_kind[def_id] <- DefKind::Macro(macro_kind.into())); + self.encode_attrs(id); - record!(self.tables.def_keys[def_id] <- def_key); - record!(self.tables.def_ident_span[def_id] <- span); - record!(self.tables.def_span[def_id] <- span); - record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_some_lazy!(self.tables.def_ident_span[def_id] <- span); + record_some_lazy!(self.tables.def_span[def_id] <- span); + record_some_lazy!(self.tables.visibility[def_id] <- ty::Visibility::Public); if let Some(stability) = stability { - record!(self.tables.lookup_stability[def_id] <- stability); + record_some_lazy!(self.tables.lookup_stability[def_id] <- stability); } } let macros = self.lazy_array(macros); - Some(ProcMacroData { proc_macro_decls_static, stability, macros }) + let mut proc_macro_quoted_spans = TableBuilder::default(); + for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { + proc_macro_quoted_spans.set_some(i, self.lazy(span)); + } + + let proc_macro_quoted_spans = proc_macro_quoted_spans.encode(&mut self.opaque); + + Some(ProcMacroData { + proc_macro_decls_static, + stability, + macros, + proc_macro_quoted_spans, + }) } else { None } @@ -2263,15 +2279,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if of_trait { let header = tcx.impl_trait_header(def_id); - record!(self.tables.impl_trait_header[def_id] <- header); + record_some_lazy!(self.tables.impl_trait_header[def_id] <- header); let impl_is_fully_generic_for_reflection = tcx.impl_is_fully_generic_for_reflection(def_id); - self.tables - .impl_is_fully_generic_for_reflection - .set(def_id.index, impl_is_fully_generic_for_reflection); - self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id)); + record_value!(self.tables.impl_is_fully_generic_for_reflection[def_id] <- impl_is_fully_generic_for_reflection); + record_value!(self.tables.defaultness[def_id] <- tcx.defaultness(def_id)); let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip(); let simplified_self_ty = fast_reject::simplify_type( @@ -2288,14 +2302,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Ok(mut an) = trait_def.ancestors(tcx, def_id) && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - self.tables.impl_parent.set_some(def_id.index, parent.into()); + record_some!(self.tables.impl_parent[def_id] <- parent.into()); } // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if tcx.is_lang_item(trait_ref.def_id, LangItem::CoerceUnsized) { let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); - record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); + record_some_lazy!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); } } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index b70151604f679..f7a437043dade 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -214,6 +214,7 @@ pub(crate) struct ProcMacroData { proc_macro_decls_static: DefIndex, stability: Option, macros: LazyArray<(DefIndex, LazyValue)>, + proc_macro_quoted_spans: LazyTableSingleIdx>>, } #[derive(MetadataEncodable, LazyDecodable)] @@ -503,7 +504,6 @@ define_tables! { // `DefPathTable` up front, since we may only ever use a few // definitions from any given crate. def_keys: Table>, - proc_macro_quoted_spans: Table>, variant_data: Table>, assoc_container: Table>, macro_definition: Table>, diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 9685ac89011e5..3a008514f7d07 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -5,7 +5,7 @@ use std::cmp::Ordering; use std::hash::Hash; -use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry, default}; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_hir::def::DefKind; use rustc_hir::{ItemKind, Node, UseKind, UseTree}; @@ -277,9 +277,9 @@ impl EffectiveVisibilities { } } -impl Default for EffectiveVisibilities { +const impl Default for EffectiveVisibilities { fn default() -> Self { - EffectiveVisibilities { map: Default::default() } + EffectiveVisibilities { map: default::fx_index_map() } } } diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs index 4ab2fbe914965..8faf6f68f49c9 100644 --- a/compiler/rustc_middle/src/query/arena_cached.rs +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -5,13 +5,15 @@ use rustc_span::ErrorGuaranteed; use crate::ty::TyCtxt; -/// Helper trait that allows `arena_cache` queries to return `Option<&T>` -/// instead of `&Option`, and avoid allocating `None` in the arena. +/// Helper trait that allows `arena_cache` queries to return: +/// - `Option<&T>` instead of `&Option`, and avoid allocating `None` in the arena; or +/// - `Result<&T, ErrorGuaranteed>` instead of `&Result`, and avoid allocating +/// `Err(ErrorGuaranteed)` in the arena. /// -/// An arena-cached query must be declared to return a type that implements -/// this trait, i.e. either `&'tcx T` or `Option<&'tcx T>`. This trait then -/// determines the types returned by the provider and stored in the arena, -/// and provides a function to bridge between the three types. +/// An arena-cached query must be declared to return a type that implements this trait, i.e. +/// `&'tcx T`, `Option<&'tcx T>`, or `Result<&'tcx T, ErrorGuaranteed>`. This trait then determines +/// the types returned by the provider and stored in the arena, and provides a function to bridge +/// between the three types. pub trait ArenaCached<'tcx>: Sized { /// Type that is returned by the query provider. type Provided; diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 4fd91caa94cd7..3e057e96ddb6b 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -1,4 +1,5 @@ -//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro. +//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` +//! proc macro. //! //! The dummy items in this module are used to enable hover documentation for //! modifier names in the query list, and to allow find-all-references to list @@ -10,10 +11,12 @@ /// # `arena_cache` query modifier /// /// Query return values must impl `Copy` and be small, but some queries must return values that -/// doesn't meet those criteria. Queries marked with this modifier have their values allocated in -/// an arena and the query returns a reference to the value. There are two cases. +/// don't meet those criteria. Queries marked with this modifier have their values allocated in +/// an arena and the query returns a reference to the value. There are three cases. /// - If the provider function returns `T` then the query will return `&'tcx T`. /// - If the provider function returns `Option` then the query will return `Option<&'tcx T>`. +/// - If the provider function returns `Result` then the query will return +/// `Result<&'tcx T, ErrorGuaranteed>`. /// /// The query plumbing takes care of the arenas and the type manipulations. pub(crate) struct arena_cache; diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 25fc0775b1a32..fc8a6de5d232e 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -1,35 +1,20 @@ -// Helpers for handling cast expressions, used in both -// typeck and codegen. +// Helpers for handling cast expressions. -use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_span::bug; use crate::mir; use crate::ty::{self, Ty}; -/// Types that are represented as ints. +/// Valid types for the result of a non-coercion cast #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum IntTy { - U(ty::UintTy), - I, +pub enum CastTy<'tcx> { + /// `iN`, `uN`, and integer inference variables. + Int, + /// C-like (fieldless) enums. CEnum, Bool, Char, -} - -impl IntTy { - pub fn is_signed(self) -> bool { - matches!(self, Self::I) - } -} - -// Valid types for the result of a non-coercion cast -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CastTy<'tcx> { - /// Various types that are represented as ints and handled mostly - /// in the same way, merged for easier matching. - Int(IntTy), - /// Floating-point types. + /// `fN` and float inference variables. Float, /// Function pointers. FnPtr, @@ -37,40 +22,28 @@ pub enum CastTy<'tcx> { Ptr(ty::TypeAndMut<'tcx>), } -/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) -/// (or rustc_hir_analysis/check/cast.rs). -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] -pub enum CastKind { - PtrPtrCast, - PtrAddrCast, - AddrPtrCast, - NumericCast, - EnumCast, - PrimIntCast, - U8CharCast, - ArrayPtrCast, - FnPtrPtrCast, - FnPtrAddrCast, -} - impl<'tcx> CastTy<'tcx> { /// Returns `Some` for integral/pointer casts. /// Casts like unsizing casts will return `None`. pub fn from_ty(t: Ty<'tcx>) -> Option> { match *t.kind() { - ty::Bool => Some(CastTy::Int(IntTy::Bool)), - ty::Char => Some(CastTy::Int(IntTy::Char)), - ty::Int(_) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), - ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), - ty::Float(_) => Some(CastTy::Float), - ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), + ty::Bool => Some(CastTy::Bool), + ty::Char => Some(CastTy::Char), + ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int), + ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), + ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::CEnum), ty::RawPtr(ty, mutbl) => Some(CastTy::Ptr(ty::TypeAndMut { ty, mutbl })), ty::FnPtr(..) => Some(CastTy::FnPtr), _ => None, } } + + pub fn is_int_like(self) -> bool { + match self { + CastTy::Int | CastTy::CEnum | CastTy::Bool | CastTy::Char => true, + CastTy::Float | CastTy::FnPtr | CastTy::Ptr(_) => false, + } + } } /// Returns `mir::CastKind` from the given parameters. @@ -78,15 +51,17 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin let from = CastTy::from_ty(from_ty); let cast = CastTy::from_ty(cast_ty); let cast_kind = match (from, cast) { - (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + (Some(from), Some(cast)) if from.is_int_like() && cast.is_int_like() => { + mir::CastKind::IntToInt + } + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int)) => { mir::CastKind::PointerExposeProvenance } - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, + (Some(CastTy::Int), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, - (Some(CastTy::Float), Some(CastTy::Int(_))) => mir::CastKind::FloatToInt, - (Some(CastTy::Int(_)), Some(CastTy::Float)) => mir::CastKind::IntToFloat, + (Some(CastTy::Float), Some(CastTy::Int)) => mir::CastKind::FloatToInt, + (Some(CastTy::Int), Some(CastTy::Float)) => mir::CastKind::IntToFloat, (Some(CastTy::Float), Some(CastTy::Float)) => mir::CastKind::FloatToFloat, (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PtrToPtr, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b6e204dd52a11..be4679320e9c7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1135,7 +1135,7 @@ impl<'tcx> TyCtxt<'tcx> { | CrateType::Cdylib | CrateType::Sdylib => false, CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, - }) + }) && !self.sess.opts.actually_rustdoc } pub fn needs_hir_hash(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 243ad73e3dfb8..925915c88cdb4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2115,28 +2115,9 @@ impl<'tcx> Ty<'tcx> { match self.kind() { ty::Bool => Some(sym::bool), ty::Char => Some(sym::char), - ty::Float(f) => match f { - ty::FloatTy::F16 => Some(sym::f16), - ty::FloatTy::F32 => Some(sym::f32), - ty::FloatTy::F64 => Some(sym::f64), - ty::FloatTy::F128 => Some(sym::f128), - }, - ty::Int(f) => match f { - ty::IntTy::Isize => Some(sym::isize), - ty::IntTy::I8 => Some(sym::i8), - ty::IntTy::I16 => Some(sym::i16), - ty::IntTy::I32 => Some(sym::i32), - ty::IntTy::I64 => Some(sym::i64), - ty::IntTy::I128 => Some(sym::i128), - }, - ty::Uint(f) => match f { - ty::UintTy::Usize => Some(sym::usize), - ty::UintTy::U8 => Some(sym::u8), - ty::UintTy::U16 => Some(sym::u16), - ty::UintTy::U32 => Some(sym::u32), - ty::UintTy::U64 => Some(sym::u64), - ty::UintTy::U128 => Some(sym::u128), - }, + ty::Int(i) => Some(i.name()), + ty::Uint(u) => Some(u.name()), + ty::Float(f) => Some(f.name()), ty::Str => Some(sym::str), _ => None, } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 43ce9634cc203..16df895b0f728 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1252,7 +1252,6 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio Linkage::WeakODR => "WeakODR", Linkage::Internal => "Internal", Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", }; output.push('['); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c841845392429..40febd26806a6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -63,6 +63,8 @@ enum CurrentGoalKind { /// These are currently the only goals whose impl where-clauses are considered to be /// productive steps. CoinductiveTrait, + /// A `ProjectionGoal`. These are temporarily special wrt to cycle handling. + Projection, // FIXME: Consider renaming `PredicateKind::NormalizesTo` to match with this /// Unlike other goals, `NormalizesTo` goals aren't independent goals but just implementation /// details for handling projections of associated terms. When we encounter a `Projection` goal @@ -89,6 +91,7 @@ impl CurrentGoalKind { CurrentGoalKind::Misc } } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => CurrentGoalKind::Projection, ty::PredicateKind::NormalizesTo(_) => { CurrentGoalKind::ProjectionComputeAssocTermCandidate } @@ -428,46 +431,29 @@ where } /// Computes the `PathKind` for the step from the current goal to the - /// nested goal required due to `source`. + /// nested goal. See #136824 for a more detailed reasoning for this why we care about + /// the step from a goal to its nested goals. /// - /// See #136824 for a more detailed reasoning for this behavior. We - /// consider cycles to be coinductive if they 'step into' a where-clause - /// of a coinductive trait. We will likely extend this function in the future - /// and will need to clearly document it in the rustc-dev-guide before - /// stabilization. - pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind { - match source { - // We treat these goals as unknown for now. It is likely that most miscellaneous - // nested goals will be converted to an inductive variant in the future. - // - // Having unknown cycles is always the safer option, as changing that to either - // succeed or hard error is backwards compatible. If we incorrectly treat a cycle - // as inductive even though it should not be, it may be unsound during coherence and - // fixing it may cause inference breakage or introduce ambiguity. - GoalSource::Misc => PathKind::Unknown, - GoalSource::NormalizeGoal(path_kind) => path_kind, - GoalSource::ImplWhereBound => match self.current_goal_kind { - // We currently only consider a cycle coinductive if it steps - // into a where-clause of a coinductive trait. - CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // We probably want to make all traits coinductive in the future, - // so we treat cycles involving where-clauses of not-yet coinductive - // traits as ambiguous for now. - CurrentGoalKind::Misc | CurrentGoalKind::ProjectionComputeAssocTermCandidate => { - PathKind::Unknown - } - }, - // Relating types is always unproductive. If we were to map proof trees to - // corecursive functions as explained in #136824, relating types never - // introduces a constructor which could cause the recursion to be guarded. - // - // FIXME(-Znext-solver=coinductive): For now we treat all inductive cycles as - // `Unknown`. See the comment in `fn initial_provisional_result`. - GoalSource::TypeRelating => PathKind::Unknown, - // These goal sources are likely unproductive and can be changed to - // `PathKind::Inductive`. Keeping them as unknown until we're confident - // about this and have an example where it is necessary. - GoalSource::AliasBoundConstCondition | GoalSource::AliasWellFormed => PathKind::Unknown, + /// For now we're entirely ignoring the reason why the current goal depends on the + /// nested goal and instead only depend on the current goal itself. This closely + /// matches the old solver. This is something we'll likely improve as we go forward + /// afterwards. + // FIXME(-Znext-solver=coinductive): Nothing interesting going on here right now + // and we're ignoring the goal source. We should change this going forward. + pub(super) fn step_kind_to_nested(&self, _source: GoalSource) -> PathKind { + match self.current_goal_kind { + // We currently consider a cycle involving a trait goal for a coinductive + // trait as coinductive as long as it otherwise only includes normalization. + CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, + // We do need to treat cycles involving only coinductive trait goals + // and normalization as coinductive due to trait-system-refactor-initiative#10. + CurrentGoalKind::ProjectionComputeAssocTermCandidate | CurrentGoalKind::Projection => { + PathKind::Unknown + } + // We probably want to make all traits coinductive in the future, + // so we treat cycles involving where-clauses of not-yet coinductive + // traits as ambiguous for now. + CurrentGoalKind::Misc => PathKind::ForcedAmbiguity, } } @@ -771,7 +757,7 @@ where let (goal, opaque_types) = self.delegate.deeply_resolve_via_unification_table((goal, opaque_types)); let typing_mode = self.typing_mode(); - let step_kind = self.step_kind_for_source(source); + let step_kind = self.step_kind_to_nested(source); let tracing_span = tracing::span!( Level::DEBUG, @@ -1109,11 +1095,8 @@ where source: GoalSource, mut goal: Goal, ) -> Result<(), NoSolutionOrRerunNonErased> { - goal.predicate = self.normalize( - GoalSource::NormalizeGoal(self.step_kind_for_source(source)), - goal.param_env, - ty::Unnormalized::new_wip(goal.predicate), - )?; + goal.predicate = + self.normalize(goal.param_env, ty::Unnormalized::new_wip(goal.predicate))?; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); if let Some(GoalEvaluation { goal, certainty, has_changed: _, stalled_on }) = @@ -1339,10 +1322,12 @@ where let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Subtype { .. } - | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { - GoalSource::TypeRelating + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { + GoalSource::Normalization } + // FIXME(-Znext-solver=coinductive): subtyping goals should + // likely be unproductive + ty::PredicateKind::Subtype { .. } => GoalSource::Misc, // FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive? ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc, p => unreachable!("unexpected nested goal in `relate`: {p:?}"), @@ -1519,9 +1504,7 @@ where match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} } - self.delegate.evaluate_const(param_env, alias_const, |ty| { - self.normalize(GoalSource::Misc, param_env, ty) - }) + self.delegate.evaluate_const(param_env, alias_const, |ty| self.normalize(param_env, ty)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1803,7 +1786,6 @@ where pub(super) fn normalize>( &mut self, - source: GoalSource, param_env: I::ParamEnv, value: ty::Unnormalized, ) -> Result { @@ -1819,6 +1801,7 @@ where let infer_term = self.next_term_infer_of_alias_kind(alias_term); let pred = ty::ProjectionClause { projection_term: alias_term, term: infer_term }; let goal = Goal::new(self.cx(), param_env, pred); + let source = GoalSource::Normalization; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); let GoalEvaluation { goal, certainty, has_changed: _, stalled_on } = self.evaluate_goal(source, goal, None)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 924a705205e1c..099c755d78427 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -118,7 +118,7 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let ty::OutlivesClause(ty, lt) = goal.predicate; - let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; + let ty = self.normalize(goal.param_env, ty::Unnormalized::new_wip(ty))?; // The normalized type can still contain non-rigid higher ranked aliases if their // normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones. @@ -456,7 +456,7 @@ where ); // We normalize the self type to be able to relate it with // types from candidates. - self.add_goal(GoalSource::TypeRelating, projection_goal)?; + self.add_goal(GoalSource::Normalization, projection_goal)?; self.try_evaluate_added_goals()?; Ok(self.deeply_resolve_ignoring_regions(normalized_term)) } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index 388b334ccf618..4203ae8c509cc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -439,7 +439,7 @@ where let term = match target_item_kind { ty::AliasTermKind::ProjectionTy { .. } => { let t = cx.type_of(target_item_def_id).instantiate(cx, target_args); - let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?; + let t = ecx.normalize(goal.param_env, t)?; t.into() } ty::AliasTermKind::ProjectionConst { def_id } @@ -447,7 +447,7 @@ where cx.const_of_item(ty::AliasConstKind::Projection { def_id }) => { let c = c.instantiate(cx, target_args); - let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?; + let c = ecx.normalize(goal.param_env, c)?; c.into() } ty::AliasTermKind::ProjectionConst { .. } => { diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs index 4481e1bc144ac..dfcc89b91a4f2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs @@ -34,14 +34,14 @@ where let actual = match free_alias.kind { ty::AliasTermKind::FreeTy { def_id } => { let free = cx.type_of(def_id.into()).instantiate(cx, free_alias.args); - let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + let free = self.normalize(goal.param_env, free)?; free.into() } ty::AliasTermKind::FreeConst { def_id } if let Some(free) = cx.const_of_item(ty::AliasConstKind::Free { def_id }) => { let free = free.instantiate(cx, free_alias.args); - let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + let free = self.normalize(goal.param_env, free)?; free.into() } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs index d519d1e538f1a..45ece5a0589fb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs @@ -45,7 +45,7 @@ where let normalized: I::Term = match inherent_kind { ty::AliasTermKind::InherentTy { def_id } => { let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args); - let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + let inherent = self.normalize(goal.param_env, inherent)?; inherent.into() } ty::AliasTermKind::InherentConstImpl { def_id } @@ -53,7 +53,7 @@ where cx.const_of_item(ty::AliasConstKind::InherentImpl { def_id }) => { let inherent = inherent.instantiate(cx, inherent_args); - let normalized_ct = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + let normalized_ct = self.normalize(goal.param_env, inherent)?; let normalized = normalized_ct.into(); let term = ty::AliasTerm::new_from_args(cx, inherent_kind, inherent_args); self.push_const_arg_has_type_goal(goal.param_env, term, normalized)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index 9d6b8875071ba..f6521e579d722 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -69,7 +69,7 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ }, - ) = self.evaluate_goal_raw(GoalSource::TypeRelating, normalizes_to)?; + ) = self.evaluate_goal_raw(GoalSource::Normalization, normalizes_to)?; trace!(?nested_goals); diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs index 2f56056449779..048c8bb45361b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs @@ -99,8 +99,7 @@ where _ => re, }) }); - let actual = - self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; } TypingMode::Coherence @@ -145,7 +144,7 @@ where _ => re, }) }); - let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) @@ -154,7 +153,7 @@ where TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => { // FIXME: Add an assertion that opaque type storage is empty. let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); - let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b92da685bc6a5..f4f0529698fa4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1425,11 +1425,11 @@ pub struct Resolver<'ra, 'tcx> { extern_module_map: RwLock>>, /// Maps glob imports to the names of items actually imported. - glob_map: FxIndexMap>, + glob_map: FxIndexMap> = default::fx_index_map(), glob_error: Option = None, visibilities_for_hashing: Vec<(LocalDefId, Visibility)> = Vec::new(), used_imports: FxHashSet = default::fx_hash_set(), - maybe_unused_trait_imports: FxIndexSet, + maybe_unused_trait_imports: FxIndexSet = default::fx_index_set(), /// Privacy errors are delayed until the end in order to deduplicate them. privacy_errors: Vec> = Vec::new(), @@ -1450,7 +1450,7 @@ pub struct Resolver<'ra, 'tcx> { builtin_macros: FxHashMap = default::fx_hash_map(), registered_attr_tools: &'tcx RegisteredTools, registered_lint_tools: &'tcx RegisteredTools, - macro_use_prelude: FxIndexMap>, + macro_use_prelude: FxIndexMap> = default::fx_index_map(), /// Eagerly populated map of all local macro definitions. local_macro_map: FxHashMap> = default::fx_hash_map(), /// Lazily populated cache of macro definitions loaded from external crates. @@ -1460,9 +1460,9 @@ pub struct Resolver<'ra, 'tcx> { non_macro_attr: &'ra Arc, local_macro_def_scopes: FxHashMap> = default::fx_hash_map(), ast_transform_scopes: FxHashMap> = default::fx_hash_map(), - unused_macros: FxIndexMap, + unused_macros: FxIndexMap = default::fx_index_map(), /// A map from the macro to all its potentially unused arms and the `LocalDefId` of the macro itself. - unused_macro_rules: FxIndexMap)>, + unused_macro_rules: FxIndexMap)> = default::fx_index_map(), proc_macro_stubs: FxHashSet = default::fx_hash_set(), /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1529,25 +1529,25 @@ pub struct Resolver<'ra, 'tcx> { /// Generic args to suggest for required params (e.g. `<'_>`, `<_, _>`), if any. item_required_generic_args_suggestions: FxHashMap = default::fx_hash_map(), delegation_fn_sigs: LocalDefIdMap = Default::default(), - delegation_infos: FxIndexMap, - delegation_inherent_fn_map: FxIndexMap>, + delegation_infos: FxIndexMap = default::fx_index_map(), + delegation_inherent_fn_map: FxIndexMap> = default::fx_index_map(), main_def: Option = None, - trait_impls: FxIndexMap>, + trait_impls: FxIndexMap> = default::fx_index_map(), /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec = Vec::new(), - paths_matching_assoc_types: UnordSet, - confused_type_with_std_module: FxIndexMap, + paths_matching_assoc_types: UnordSet = Default::default(), + confused_type_with_std_module: FxIndexMap = default::fx_index_map(), /// Names of items that were stripped out via cfg with their corresponding cfg meta item. stripped_cfg_items: Vec> = Vec::new(), - effective_visibilities: EffectiveVisibilities, - macro_reachable_adts: FxIndexMap>, + effective_visibilities: EffectiveVisibilities = Default::default(), + macro_reachable_adts: FxIndexMap> = default::fx_index_map(), - doc_link_resolutions: FxIndexMap, - doc_link_traits_in_scope: FxIndexMap>, + doc_link_resolutions: FxIndexMap = default::fx_index_map(), + doc_link_traits_in_scope: FxIndexMap> = default::fx_index_map(), all_macro_rules: UnordSet = Default::default(), /// Invocation ids of all glob delegations. @@ -1860,9 +1860,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { local_module_map, extern_module_map: Default::default(), - glob_map: Default::default(), - maybe_unused_trait_imports: Default::default(), - arenas, dummy_decl: arenas.new_pub_def_decl(Res::Err, DUMMY_SP, LocalExpnId::ROOT), builtin_type_decls: PrimTy::ALL @@ -1891,31 +1888,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect(), registered_attr_tools, registered_lint_tools, - macro_use_prelude: Default::default(), extern_macro_map: Default::default(), dummy_ext_bang: arenas.alloc_macro(SyntaxExtension::dummy_bang(edition)), dummy_ext_derive: arenas.alloc_macro(SyntaxExtension::dummy_derive(edition)), non_macro_attr: arenas.alloc_macro(SyntaxExtension::non_macro_attr(edition)), - unused_macros: Default::default(), - unused_macro_rules: Default::default(), single_segment_macro_resolutions: Default::default(), multi_segment_macro_resolutions: Default::default(), lint_buffer: LintBuffer::default(), owners, current_owner: PerOwnerResolverData::new(DUMMY_NODE_ID, CRATE_DEF_ID), invocation_parents, - trait_impls: Default::default(), - confused_type_with_std_module: Default::default(), - paths_matching_assoc_types: Default::default(), - stripped_cfg_items: Default::default(), - effective_visibilities: Default::default(), - macro_reachable_adts: Default::default(), - doc_link_resolutions: Default::default(), - doc_link_traits_in_scope: Default::default(), current_crate_outer_attr_insert_span, disambiguators: Default::default(), - delegation_infos: Default::default(), - delegation_inherent_fn_map: Default::default(), features: tcx.features(), .. }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3debf3248ff80..a003f20e3512d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -671,7 +671,6 @@ symbols! { cold_path, collapse_debuginfo, column, - common, compare_bytes, compare_exchange, compare_exchange_weak, @@ -1176,7 +1175,6 @@ symbols! { integer_max, integer_min, integral, - internal, internal_eq_trait_method_impls, internal_features, interrupt, diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 0e3ccf33c3ffe..e444e605d3b5a 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -1,6 +1,7 @@ use arrayvec::ArrayVec; use rustc_abi::{ - BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, + BackendRepr, FieldsShape, Float, HasDataLayout, Integer, Numeric, Primitive, Reg, RegKind, + Size, TyAbiInterface, }; use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; @@ -56,6 +57,22 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { + // NOTE: Complex is returned indirectly. + if let Some(component) = ret.layout.complex_number(cx) { + match component { + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + // Return a Complex<{integer}> packed into a single register when that fits. + ret.cast_to(Reg { kind: RegKind::Integer, size }); + } + _ => { + // Otherwise pass in 2 registers. + let reg = Reg { kind: component.reg_kind(), size: component.size() }; + ret.cast_to(CastTarget::pair(reg, reg)); + } + } + return; + } + // Unlike other architectures which return aggregates in registers, MIPS n64 limits the // use of float registers to structures (not unions) containing exactly one or two // float fields. @@ -103,6 +120,52 @@ where extend_integer_width_mips(arg, 64); } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); + } else if let Some(component) = arg.layout.complex_number(cx) + && !matches!(component, Numeric::Float(Float::F16)) + { + let slot = dl.pointer_size(); + let curr_offset = offset.align_to(align); + + const NUM_ARG_SLOTS: u64 = 8; + + match component { + Numeric::Float(Float::F16B) => unreachable!("Complex is not C-compatible"), + Numeric::Float(Float::F16) => unreachable!("not supported on mips64"), + Numeric::Float(Float::F32 | Float::F64) => { + // Only pass a Complex/Complex in FPRs when two argument slots are free. + if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS { + // Both components claim a slot, even a Complex which could fit in one + // slot. + // + // FIXME(complex_numbers): c-variadic arguments are passed in GPRs, so need a + // special carve-out here and Complex is bitpacked into one 64-bit GPR. + *offset = curr_offset + slot * 2; + let unit = Reg { kind: RegKind::Float, size: component.size() }; + let cast_target = CastTarget::from(Uniform::new(unit, size)); + arg.cast_to(cast_target); + return; + } + + // Otherwise pack it into GPRs (or the stack) like an integer of the same size. + arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32); + } + Numeric::Float(Float::F128) => { + // Complex is passed in 4 FPRs, but aligned to 16 so may need padding. + let reg = Reg { kind: RegKind::Float, size: arg.layout.field(cx, 0).size }; + arg.cast_to_and_pad_i32(CastTarget::pair(reg, reg), pad_i32); + } + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + // Cast Complex into i16, Complex to i32, etc. + let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); + // The inreg attribute makes the bits land in the right (upper) bits on BE targets. + arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into())); + } + Numeric::Int(Integer::I64 | Integer::I128, _) => { + // Complex and Complex are passed as 2 separate arguments. + let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); + arg.cast_to(cast_target); + } + } } else { match arg.layout.fields { FieldsShape::Primitive => unreachable!(), diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index edc23b6c50b45..487de8f54ee62 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -733,17 +733,17 @@ impl<'a, Ty> FnAbi<'a, Ty> { let spec = cx.target_spec(); match &spec.arch { Arch::X86 => { - let (flavor, regparm) = match abi { + let flavor = match abi { ExternAbi::Fastcall { .. } | ExternAbi::Vectorcall { .. } => { - (x86::Flavor::FastcallOrVectorcall, None) + x86::Flavor::FastcallOrVectorcall } ExternAbi::C { .. } | ExternAbi::Cdecl { .. } | ExternAbi::Stdcall { .. } => { - (x86::Flavor::General, cx.x86_abi_opt().regparm) + x86::Flavor::General { regparam: cx.x86_abi_opt().regparm } } - _ => (x86::Flavor::General, None), + _ => x86::Flavor::General { regparam: None }, }; let reg_struct_return = cx.x86_abi_opt().reg_struct_return; - let opts = x86::X86Options { flavor, regparm, reg_struct_return }; + let opts = x86::X86Options { flavor, reg_struct_return }; if spec.is_like_msvc { x86_win32::compute_abi_info(cx, self, opts); } else { diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 3476f41e3bc75..efe88bff6504c 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -2,7 +2,7 @@ use rustc_abi::{ AddressSpace, Align, BackendRepr, Float, HasDataLayout, Primitive, Reg, RegKind, TyAndLayout, }; -use crate::callconv::{ArgAttribute, FnAbi, PassMode, TyAbiInterface}; +use crate::callconv::{ArgAbi, ArgAttribute, FnAbi, PassMode, TyAbiInterface}; use crate::spec::{HasTargetSpec, RustcAbi}; /// Is this a struct with a single float field? @@ -37,65 +37,135 @@ where } } -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub(crate) enum Flavor { - General, + General { regparam: Option }, FastcallOrVectorcall, } +#[derive(Clone, Copy)] pub(crate) struct X86Options { pub flavor: Flavor, - pub regparm: Option, pub reg_struct_return: bool, } -pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) +fn classify_ret<'a, Ty, C>(cx: &C, opts: X86Options, ret: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if !fn_abi.ret.is_ignore() { - if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() { - // Returning a structure. Most often, this will use - // a hidden first argument. On some platforms, though, - // small structs are returned as integers. - // - // Some links: - // https://www.angelcode.com/dev/callconv/callconv.html - // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp - let t = cx.target_spec(); - if let Some(Float::F16) = fn_abi.ret.layout.complex_float(cx) { - // `_Complex _Float16` is returned as `<2 x half>`. - let kind = RegKind::Vector { hint_vector_elem: Primitive::Float(Float::F16) }; - fn_abi.ret.cast_to(Reg { kind, size: fn_abi.ret.layout.size }); - } else if t.abi_return_struct_as_int - || opts.reg_struct_return - || fn_abi.ret.layout.is_complex_number(cx) - { - // According to Clang, everyone but MSVC returns single-element - // float aggregates directly in a floating-point register. - if is_single_fp_element(fn_abi.ret.layout, cx) { - match fn_abi.ret.layout.size.bytes() { - 2 => fn_abi.ret.cast_to(Reg::f16()), - 4 => fn_abi.ret.cast_to(Reg::f32()), - 8 => fn_abi.ret.cast_to(Reg::f64()), - _ => fn_abi.ret.make_indirect(), - } - } else { - match fn_abi.ret.layout.size.bytes() { - 1 => fn_abi.ret.cast_to(Reg::i8()), - 2 => fn_abi.ret.cast_to(Reg::i16()), - 4 => fn_abi.ret.cast_to(Reg::i32()), - 8 => fn_abi.ret.cast_to(Reg::i64()), - _ => fn_abi.ret.make_indirect(), - } + if ret.layout.is_aggregate() && ret.layout.is_sized() { + // Returning a structure. Most often, this will use + // a hidden first argument. On some platforms, though, + // small structs are returned as integers. + // + // Some links: + // https://www.angelcode.com/dev/callconv/callconv.html + // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp + let t = cx.target_spec(); + if let Some(Float::F16) = ret.layout.complex_float(cx) { + // `_Complex _Float16` is returned as `<2 x half>`. + let kind = RegKind::Vector { hint_vector_elem: Primitive::Float(Float::F16) }; + ret.cast_to(Reg { kind, size: ret.layout.size }); + } else if t.abi_return_struct_as_int + || opts.reg_struct_return + || ret.layout.is_complex_number(cx) + { + // According to Clang, everyone but MSVC returns single-element + // float aggregates directly in a floating-point register. + if is_single_fp_element(ret.layout, cx) { + match ret.layout.size.bytes() { + 2 => ret.cast_to(Reg::f16()), + 4 => ret.cast_to(Reg::f32()), + 8 => ret.cast_to(Reg::f64()), + _ => ret.make_indirect(), } } else { - fn_abi.ret.make_indirect(); + match ret.layout.size.bytes() { + 1 => ret.cast_to(Reg::i8()), + 2 => ret.cast_to(Reg::i16()), + 4 => ret.cast_to(Reg::i32()), + 8 => ret.cast_to(Reg::i64()), + _ => ret.make_indirect(), + } } } else { - fn_abi.ret.extend_integer_width_to(32); + ret.make_indirect(); } + } else { + ret.extend_integer_width_to(32); + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + let t = cx.target_spec(); + let align_4 = Align::from_bytes(4).unwrap(); + let align_16 = Align::from_bytes(16).unwrap(); + + if arg.layout.is_aggregate() { + // We need to compute the alignment of the `byval` argument. The rules can be found in + // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized + // here, they are: + // + // 1. If the natural alignment of the type is <= 4, the alignment is 4. + // + // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment. + // This doesn't matter here because we only pass aggregates via `byval`, not vectors. + // + // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector + // type is 16. + // + // 4. If none of these conditions are true, the alignment is 4. + + fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match layout.backend_repr { + BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false, + BackendRepr::SimdVector { .. } => true, + BackendRepr::Memory { .. } => { + for i in 0..layout.fields.count() { + if contains_vector(cx, layout.field(cx, i)) { + return true; + } + } + false + } + BackendRepr::SimdScalableVector { .. } => { + panic!("scalable vectors are unsupported") + } + } + } + + let byval_align = if arg.layout.align.abi < align_4 { + // (1.) + align_4 + } else if t.is_like_darwin && contains_vector(cx, arg.layout) { + // (3.) + align_16 + } else { + // (4.) + align_4 + }; + + arg.pass_by_stack_offset(Some(byval_align)); + } else { + arg.extend_integer_width_to(32); + } +} + +pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, opts, &mut fn_abi.ret); } for arg in fn_abi.args.iter_mut() { @@ -108,61 +178,7 @@ where continue; } - let t = cx.target_spec(); - let align_4 = Align::from_bytes(4).unwrap(); - let align_16 = Align::from_bytes(16).unwrap(); - - if arg.layout.is_aggregate() { - // We need to compute the alignment of the `byval` argument. The rules can be found in - // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized - // here, they are: - // - // 1. If the natural alignment of the type is <= 4, the alignment is 4. - // - // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment. - // This doesn't matter here because we only pass aggregates via `byval`, not vectors. - // - // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector - // type is 16. - // - // 4. If none of these conditions are true, the alignment is 4. - - fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool - where - Ty: TyAbiInterface<'a, C> + Copy, - { - match layout.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false, - BackendRepr::SimdVector { .. } => true, - BackendRepr::Memory { .. } => { - for i in 0..layout.fields.count() { - if contains_vector(cx, layout.field(cx, i)) { - return true; - } - } - false - } - BackendRepr::SimdScalableVector { .. } => { - panic!("scalable vectors are unsupported") - } - } - } - - let byval_align = if arg.layout.align.abi < align_4 { - // (1.) - align_4 - } else if t.is_like_darwin && contains_vector(cx, arg.layout) { - // (3.) - align_16 - } else { - // (4.) - align_4 - }; - - arg.pass_by_stack_offset(Some(byval_align)); - } else { - arg.extend_integer_width_to(32); - } + classify_arg(cx, arg); } fill_inregs(cx, fn_abi, opts, false); @@ -176,9 +192,6 @@ pub(crate) fn fill_inregs<'a, Ty, C>( ) where Ty: TyAbiInterface<'a, C> + Copy, { - if opts.flavor != Flavor::FastcallOrVectorcall && opts.regparm.is_none_or(|x| x == 0) { - return; - } // Mark arguments as InReg like clang does it, // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall. @@ -188,8 +201,19 @@ pub(crate) fn fill_inregs<'a, Ty, C>( // IsSoftFloatABI is only set to true on ARM platforms, // which in turn can't be x86? - // 2 for fastcall/vectorcall, regparm limited by 3 otherwise - let mut free_regs = opts.regparm.unwrap_or(2).into(); + // The number of registers available for argument passing. + // + // An `extern "fastcall"` and `extern "vectorcall"` function always have 2 registers available. + // Otherwise the `regparam` count (in the range 0..=3) determines the number of available + // registers. If unspecified, no registers are used for argument passing. + let mut free_regs = match opts.flavor { + Flavor::FastcallOrVectorcall => 2, + Flavor::General { regparam } => u64::from(regparam.unwrap_or(0)), + }; + + if free_regs == 0 { + return; + } // For types generating PassMode::Cast, InRegs will not be set. // Maybe, this is a FIXME diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 5ab2834807d67..57e997f45d4c5 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -131,17 +131,16 @@ where } fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option { - if *i >= cls.len() { + let Some(Some(class)) = cls.get(*i) else { return None; - } + }; - match cls[*i] { - None => None, - Some(Class::Int) => { + match class { + Class::Int => { *i += 1; Some(if size.bytes() < 8 { Reg { kind: RegKind::Integer, size } } else { Reg::i64() }) } - Some(Class::Sse) => { + Class::Sse => { let vec_len = 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count(); *i += vec_len; @@ -154,7 +153,7 @@ fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option unreachable!("reg_component: unhandled class {:?}", c), + c => unreachable!("reg_component: unhandled class {:?}", c), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index d30421a762ea9..5ccd7ff7ef55b 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -510,7 +510,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { match (child_mode, nested_goal.source()) { ( ChildMode::Trait(_) | ChildMode::Host(_), - GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + GoalSource::Misc | GoalSource::Normalization, ) => { continue; } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index c6d88bb6603de..5d1472d5941f2 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -16,7 +16,6 @@ use tracing::debug; use crate::inherent::*; use crate::lang_items::SolverTraitLangItem; use crate::region_constraint::RegionConstraint; -use crate::search_graph::PathKind; use crate::{ self as ty, Canonical, CanonicalVarValues, CantBeErased, Const, ConstVid, FloatVid, GenericArgKind, InferConst, IntVid, Interner, TermKind, TyVid, TypingMode, Upcast, @@ -411,22 +410,15 @@ impl Goal { /// Why a specific goal has to be proven. /// -/// This is necessary as we treat nested goals different depending on -/// their source. This is used to decide whether a cycle is coinductive. -/// See the documentation of `EvalCtxt::step_kind_for_source` for more details -/// about this. +/// This is used by proof tree visitors, especially for diagnostics purposes. /// -/// It is also used by proof tree visitors, e.g. for diagnostics purposes. +/// FIXME(-Znext-solver=coinductive): This will also matter in the future when +/// deciding whether a step in a cycle is coinductive. We're currently still +/// matching the old solver behavior here for now, so the `GoalSource` is ignored. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum GoalSource { Misc, - /// A nested goal required to prove that types are equal/subtypes. - /// This is always an unproductive step. - /// - /// This is also used for all `NormalizesTo` goals as we they are used - /// to relate types in `AliasRelate`. - TypeRelating, /// We're proving a where-bound of an impl. ImplWhereBound, /// Const conditions that need to hold for `[const]` alias bounds to hold. @@ -438,12 +430,8 @@ pub enum GoalSource { /// 2. for rigid projections's trait goal, /// 3. for GAT where clauses. AliasWellFormed, - /// In case normalizing aliases in nested goals cycles, eagerly normalizing these - /// aliases in the context of the parent may incorrectly change the cycle kind. - /// Normalizing aliases in goals therefore tracks the original path kind for this - /// nested goal. See the comment of the `ReplaceAliasWithInfer` visitor for more - /// details. - NormalizeGoal(PathKind), + /// Normalizing happens in the current context and is unproductive by itself. + Normalization, } #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, Goal)] diff --git a/library/alloc/src/rcs/rc.rs b/library/alloc/src/rcs/rc.rs index 8cca09d1d4a8c..04e08c47fb587 100644 --- a/library/alloc/src/rcs/rc.rs +++ b/library/alloc/src/rcs/rc.rs @@ -1985,10 +1985,55 @@ impl Rc { unsafe { drop(Rc::from_raw_in(ptr, alloc)) }; } - /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to - /// this allocation. + /// Determine whether this is the unique reference to the underlying data. + /// + /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to the same allocation; + /// returns `false` otherwise. + /// + /// If this function returns `true`, it is safe to call [`get_mut_unchecked`] + /// immediately afterward. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::rc::Rc; + /// + /// let x = Rc::new(3); + /// assert!(Rc::is_unique(&x)); + /// + /// let y = Rc::clone(&x); + /// assert!(!Rc::is_unique(&x)); + /// drop(y); + /// + /// // Weak references also count, because they could be upgraded at any time. + /// let z = Rc::downgrade(&x); + /// assert!(!Rc::is_unique(&x)); + /// ``` + /// + /// # Pointer invalidation + /// + /// This function will always return the same value as `Rc::get_mut(rc).is_some()`. However, + /// unlike that operation it does not produce any mutable references to the underlying data, + /// meaning no pointers to the data inside the `Rc` are invalidated by the call. Thus, the + /// following code is valid, even though it would be UB if it used `Rc::get_mut`: + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::rc::Rc; + /// + /// let rc = Rc::new(5); + /// let pointer: *const i32 = &*rc; + /// assert!(Rc::is_unique(&rc)); + /// assert_eq!(unsafe { *pointer }, 5); + /// ``` + /// + /// [`get_mut_unchecked`]: Self::get_mut_unchecked #[inline] - fn is_unique(this: &Self) -> bool { + #[unstable(feature = "arc_is_unique", issue = "138938")] + pub fn is_unique(this: &Self) -> bool { Rc::weak_count(this) == 0 && Rc::strong_count(this) == 1 } diff --git a/library/core/src/attribute_docs.rs b/library/core/src/attribute_docs.rs index 97629be8ec6ef..9a70ea74345be 100644 --- a/library/core/src/attribute_docs.rs +++ b/library/core/src/attribute_docs.rs @@ -189,7 +189,7 @@ const _: () = (); /// /// For more information, see the Reference on [the `cfg` attribute]. /// -/// [`cfg_attr`]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +/// [`cfg_attr`]: ./attribute.cfg_attr.html /// [the `cfg` attribute]: ../reference/conditional-compilation.html#the-cfg-attribute /// [`if`]: ./keyword.if.html const _: () = (); @@ -707,3 +707,48 @@ const _: () = (); /// [Clippy]: https://github.com/rust-lang/rust-clippy /// [the `automatically_derived` attribute]: ../reference/attributes/derive.html#the-automatically_derived-attribute const _: () = (); + +#[doc(attribute = "cfg_attr")] +// +/// The `cfg_attr` attribute is used to conditionally apply one or more attributes to an item. +/// +/// Example: +/// +/// ```rust +/// // The struct derives `Debug` when the `debug_impls` feature is enabled. +/// #[cfg_attr(feature = "debug_impls", derive(Debug))] +/// struct X; +/// ``` +/// +/// You can apply multiple attributes by separating them with commas: +/// +/// ```rust +/// #[cfg_attr(feature = "nightly", allow(dead_code), deny(unused_variables))] +/// // This function only gets the `allow(dead_code)` and `deny(unused_variables)` attributes when +/// // `feature = "nightly"` is active. +/// fn nightly_only_function() { +/// let x = 42; +/// } +/// ``` +/// +/// For complex conditions, you can combine `all(...)`, `any(...)`, and `not(...)`. +/// +/// * `all`: True if all given predicates are true. +/// * `any`: True if at least one of the given predicates is true. +/// * `not`: True if the predicate is false. +/// +/// ```rust +/// #[cfg_attr( +/// all(feature = "system", feature = "disk"), +/// doc = "These docs only show up if both `system`, and `disk` are enabled.", +/// )] +/// mod my_module { +/// // ... +/// } +/// ``` +/// +/// For more information, see the Reference on [the `cfg_attr` attribute]. +/// +/// [`cfg`]: ./attribute.cfg.html +/// [the `cfg_attr` attribute]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +const _: () = (); diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index db05d7fdc5087..e53e4df1ea43d 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1544,9 +1544,10 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "clamp_magnitude", issue = "148519")] - #[must_use = "this returns the clamped value and does not modify the original"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f128) -> f128 { + pub const fn clamp_magnitude(self, limit: f128) -> f128 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 273ef3688ca5f..4ad41bd6cfa4b 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1530,9 +1530,10 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "clamp_magnitude", issue = "148519")] - #[must_use = "this returns the clamped value and does not modify the original"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f16) -> f16 { + pub const fn clamp_magnitude(self, limit: f16) -> f16 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index d3dea38dc2fcf..2dc3ddf31cac5 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1699,11 +1699,11 @@ impl f32 { /// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0); /// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0); /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f32) -> f32 { + pub const fn clamp_magnitude(self, limit: f32) -> f32 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 7c5082749cd11..cd3cfe1bd4c60 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1677,11 +1677,11 @@ impl f64 { /// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0); /// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0); /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f64) -> f64 { + pub const fn clamp_magnitude(self, limit: f64) -> f64 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 4cbd012e0021b..058c0229aa530 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3961,14 +3961,15 @@ macro_rules! int_impl { /// ``` /// #![feature(clamp_magnitude)] #[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")] - #[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")] + #[doc = concat!("assert_eq!((-120", stringify!($SelfT), ").clamp_magnitude(100), -100);")] #[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")] - #[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")] + #[doc = concat!("assert_eq!((-80", stringify!($SelfT), ").clamp_magnitude(100), -80);")] /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] - pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self { + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + pub const fn clamp_magnitude(self, limit: $UnsignedT) -> Self { if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) { self.clamp(-limit, limit) } else { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 355e9a3c59da7..c97e6f4c45395 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -2612,6 +2612,50 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } } + /// Clamps this number to a symmetric range centred around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// # use std::num::NonZero; + /// # + #[doc = concat!("let limit = NonZero::<", stringify!($Uint), ">::new(100).unwrap();")] + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(100).unwrap(),")] + /// ); + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-100).unwrap(),")] + /// ); + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap(),")] + /// ); + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap(),")] + /// ); + /// ``` + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + pub const fn clamp_magnitude(self, limit: NonZero<$Uint>) -> Self { + // SAFETY: a non-zero value clamped to the magnitude of a non-zero value is still non-zero. + unsafe { Self::new_unchecked(self.get().clamp_magnitude(limit.get())) } + } + }; } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1334b551217a8..fc2430a10edb4 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(cfg_overflow_checks)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] +#![feature(clamp_magnitude)] #![feature(clamp_to)] #![feature(clone_to_uninit)] #![feature(cmp_minmax)] diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs index 0f96e55f6914e..fdb605780a2c6 100644 --- a/library/coretests/tests/num/clamp_magnitude.rs +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -1,3 +1,7 @@ +use core::num::*; + +use crate::num::assert_biteq; + macro_rules! check_int_clamp { ($t:ty, $ut:ty) => { let min = <$t>::MIN; @@ -32,6 +36,16 @@ macro_rules! check_int_clamp { // Limit larger than type max (uN > iN::MAX) assert_eq!(max.clamp_magnitude(max_u), max); assert_eq!(min.clamp_magnitude(max_u), min); + + // Const clamping + const C1: $t = (100 as $t).clamp_magnitude(50); + assert_eq!(C1, 50); + const C2: $t = (-100 as $t).clamp_magnitude(50); + assert_eq!(C2, -50); + const C3: $t = (30 as $t).clamp_magnitude(50); + assert_eq!(C3, 30); + const C4: $t = (-30 as $t).clamp_magnitude(50); + assert_eq!(C4, -30); }; } @@ -65,8 +79,81 @@ fn test_clamp_magnitude_isize() { check_int_clamp!(isize, usize); } +macro_rules! check_nonzero_clamp { + ($t:ty, $ut:ty) => { + let min = <$t>::MIN; + let max = <$t>::MAX; + let max_u = <$ut>::MAX; + + // Basic clamping + let lim = <$ut>::new(50).unwrap(); + assert_eq!(<$t>::new(100).unwrap().clamp_magnitude(lim), <$t>::new(50).unwrap()); + assert_eq!(<$t>::new(-100).unwrap().clamp_magnitude(lim), <$t>::new(-50).unwrap()); + assert_eq!(<$t>::new(30).unwrap().clamp_magnitude(lim), <$t>::new(30).unwrap()); + assert_eq!(<$t>::new(-30).unwrap().clamp_magnitude(lim), <$t>::new(-30).unwrap()); + + // Exact boundary + assert_eq!(<$t>::new(50).unwrap().clamp_magnitude(lim), <$t>::new(50).unwrap()); + assert_eq!(<$t>::new(-50).unwrap().clamp_magnitude(lim), <$t>::new(-50).unwrap()); + + // MIN/MAX values + // Symmetric range [-MAX, MAX] + assert_eq!(max.clamp_magnitude(max.try_into().unwrap()), max); + assert_eq!(min.clamp_magnitude(max.try_into().unwrap()), -max); + + // Full range (limit covers MIN) + let min_abs = min.unsigned_abs(); + assert_eq!(min.clamp_magnitude(min_abs), min); + + // Limit larger than type max (uN > iN::MAX) + assert_eq!(max.clamp_magnitude(max_u), max); + assert_eq!(min.clamp_magnitude(max_u), min); + + // Const clamping + const LIM: $ut = <$ut>::new(50).unwrap(); + const C1: $t = <$t>::new(100).unwrap().clamp_magnitude(LIM); + assert_eq!(C1, <$t>::new(50).unwrap()); + const C2: $t = <$t>::new(-100).unwrap().clamp_magnitude(LIM); + assert_eq!(C2, <$t>::new(-50).unwrap()); + const C3: $t = <$t>::new(30).unwrap().clamp_magnitude(LIM); + assert_eq!(C3, <$t>::new(30).unwrap()); + const C4: $t = <$t>::new(-30).unwrap().clamp_magnitude(LIM); + assert_eq!(C4, <$t>::new(-30).unwrap()); + }; +} + +#[test] +fn test_clamp_magnitude_nonzero_i8() { + check_nonzero_clamp!(NonZeroI8, NonZeroU8); +} + +#[test] +fn test_clamp_magnitude_nonzero_i16() { + check_nonzero_clamp!(NonZeroI16, NonZeroU16); +} + +#[test] +fn test_clamp_magnitude_nonzero_i32() { + check_nonzero_clamp!(NonZeroI32, NonZeroU32); +} + +#[test] +fn test_clamp_magnitude_nonzero_i64() { + check_nonzero_clamp!(NonZeroI64, NonZeroU64); +} + +#[test] +fn test_clamp_magnitude_nonzero_i128() { + check_nonzero_clamp!(NonZeroI128, NonZeroU128); +} + +#[test] +fn test_clamp_magnitude_nonzero_isize() { + check_nonzero_clamp!(NonZeroIsize, NonZeroUsize); +} + macro_rules! check_float_clamp { - ($t:ty) => { + ($t:ty, $huge: expr) => { // Basic clamping assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0); assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0); @@ -78,10 +165,13 @@ macro_rules! check_float_clamp { assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0); // Zero cases - assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0); - assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0); - assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0); - assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0); + type Float = $t; + assert_biteq!((0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_biteq!((-0.0 as $t).clamp_magnitude(1.0), -0.0); + assert_biteq!((5.0 as $t).clamp_magnitude(0.0), 0.0); + assert_biteq!((-5.0 as $t).clamp_magnitude(0.0), -0.0); + assert_biteq!((5.0 as $t).clamp_magnitude(-0.0), 0.0); + assert_biteq!((-5.0 as $t).clamp_magnitude(-0.0), -0.0); // Special values - Infinity let inf = <$t>::INFINITY; @@ -98,42 +188,95 @@ macro_rules! check_float_clamp { let max = <$t>::MAX; let min = <$t>::MIN; // Large limit - let huge = 1e30; - assert_eq!(max.clamp_magnitude(huge), huge); - assert_eq!(min.clamp_magnitude(huge), -huge); + assert_eq!(max.clamp_magnitude($huge), $huge); + assert_eq!(min.clamp_magnitude($huge), -$huge); + + // NaN + let nan = <$t>::NAN; + assert!(nan.clamp_magnitude(1.0).is_nan()); + + // Const clamping + const C1: $t = (5.0 as $t).clamp_magnitude(3.0); + assert_eq!(C1, 3.0); + const C2: $t = (-5.0 as $t).clamp_magnitude(3.0); + assert_eq!(C2, -3.0); + const C3: $t = (2.0 as $t).clamp_magnitude(3.0); + assert_eq!(C3, 2.0); + const C4: $t = (-2.0 as $t).clamp_magnitude(3.0); + assert_eq!(C4, -2.0); }; } +#[test] +#[cfg(target_has_reliable_f16)] +fn test_clamp_magnitude_f16() { + check_float_clamp!(f16, 1e3); +} + #[test] fn test_clamp_magnitude_f32() { - check_float_clamp!(f32); + check_float_clamp!(f32, 1e30); } #[test] fn test_clamp_magnitude_f64() { - check_float_clamp!(f64); + check_float_clamp!(f64, 1e300); +} + +#[test] +#[cfg(target_has_reliable_f128)] +fn test_clamp_magnitude_f128() { + check_float_clamp!(f128, 1e3000); } #[test] -#[should_panic(expected = "limit must be non-negative")] +#[cfg(target_has_reliable_f16)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f16_panic_negative_limit() { + let _ = 1.0f16.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_negative_limit() { let _ = 1.0f32.clamp_magnitude(-1.0); } #[test] -#[should_panic(expected = "limit must be non-negative")] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f64_panic_negative_limit() { let _ = 1.0f64.clamp_magnitude(-1.0); } #[test] -#[should_panic] +#[cfg(target_has_reliable_f128)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f128_panic_negative_limit() { + let _ = 1.0f128.clamp_magnitude(-1.0); +} + +#[test] +#[cfg(target_has_reliable_f16)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f16_panic_nan_limit() { + let _ = 1.0f16.clamp_magnitude(f16::NAN); +} + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_nan_limit() { let _ = 1.0f32.clamp_magnitude(f32::NAN); } #[test] -#[should_panic] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f64_panic_nan_limit() { let _ = 1.0f64.clamp_magnitude(f64::NAN); } + +#[test] +#[cfg(target_has_reliable_f128)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f128_panic_nan_limit() { + let _ = 1.0f128.clamp_magnitude(f128::NAN); +} diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index b1c3001790f07..f892734d8b95d 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -24,6 +24,7 @@ mod u8; mod bignum; mod carryless_mul; mod cast; +mod clamp_magnitude; mod complex; mod const_from; mod dec2flt; diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index dd6e4a690e326..0520da62ffae9 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -87,7 +87,7 @@ enum AddressKind<'a> { /// return /// } /// }; -/// let addr = socket.local_addr().expect("Couldn't get local address"); +/// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// ``` #[derive(Clone)] #[stable(feature = "unix_socket", since = "1.10.0")] @@ -188,7 +188,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixListener::bind("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.is_unnamed(), false); /// Ok(()) /// } @@ -202,7 +202,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.is_unnamed(), true); /// Ok(()) /// } @@ -226,7 +226,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixListener::bind("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); /// Ok(()) /// } @@ -240,7 +240,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.as_pathname(), None); /// Ok(()) /// } diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index e03ecd7eed9ea..f80a3a887c54d 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -273,7 +273,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// let sock_copy = sock.try_clone().expect("`UnixDatagram::try_clone` should not fail"); /// Ok(()) /// } /// ``` @@ -292,7 +292,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// let addr = sock.local_addr().expect("`UnixDatagram::local_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -317,7 +317,7 @@ impl UnixDatagram { /// let sock = UnixDatagram::unbound()?; /// sock.connect("/path/to/the/socket")?; /// - /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// let addr = sock.peer_addr().expect("`UnixDatagram::peer_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -390,7 +390,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::bind("/path/to/the/socket")?; /// let mut buf = vec![0; 10]; - /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// sock.recv(buf.as_mut_slice()).expect("`UnixDatagram::recv` should not fail"); /// Ok(()) /// } /// ``` @@ -523,7 +523,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("`UnixDatagram::send_to` should not fail"); /// Ok(()) /// } /// ``` @@ -561,7 +561,7 @@ impl UnixDatagram { /// let addr = bound.local_addr()?; /// /// let sock = UnixDatagram::unbound()?; - /// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("send_to_addr function failed"); + /// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("`UnixDatagram::send_to_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -595,8 +595,8 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.connect("/some/sock").expect("Couldn't connect"); - /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// sock.connect("/some/sock").expect("`UnixDatagram::connect` should not fail"); + /// sock.send(b"omelette au fromage").expect("`UnixDatagram` is not connected"); /// Ok(()) /// } /// ``` @@ -638,7 +638,7 @@ impl UnixDatagram { /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// sock.send_vectored_with_ancillary_to(bufs, &mut ancillary, "/some/sock") - /// .expect("send_vectored_with_ancillary_to function failed"); + /// .expect("`UnixDatagram::send_vectored_with_ancillary_to` should not fail"); /// Ok(()) /// } /// ``` @@ -686,7 +686,7 @@ impl UnixDatagram { /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// sock.send_vectored_with_ancillary(bufs, &mut ancillary) - /// .expect("send_vectored_with_ancillary function failed"); + /// .expect("`UnixDatagram::send_vectored_with_ancillary` should not fail"); /// Ok(()) /// } /// ``` @@ -719,7 +719,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_read_timeout(Some(Duration::new(1, 0))) - /// .expect("set_read_timeout function failed"); + /// .expect("`UnixDatagram::set_read_timeout` should not fail"); /// Ok(()) /// } /// ``` @@ -765,7 +765,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("set_write_timeout function failed"); + /// .expect("`UnixDatagram::set_write_timeout` should not fail"); /// Ok(()) /// } /// ``` @@ -804,7 +804,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_read_timeout(Some(Duration::new(1, 0))) - /// .expect("set_read_timeout function failed"); + /// .expect("`UnixDatagram::set_read_timeout` should not fail"); /// assert_eq!(sock.read_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -826,7 +826,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("set_write_timeout function failed"); + /// .expect("`UnixDatagram::set_write_timeout` should not fail"); /// assert_eq!(sock.write_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -846,7 +846,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// sock.set_nonblocking(true).expect("`UnixDatagram::set_nonblocking` should not fail"); /// Ok(()) /// } /// ``` @@ -914,7 +914,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// sock.shutdown(Shutdown::Both).expect("`UnixDatagram::shutdown` should not fail"); /// Ok(()) /// } /// ``` @@ -941,7 +941,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::bind("/tmp/sock")?; /// let mut buf = [0; 10]; - /// let len = socket.peek(&mut buf).expect("peek failed"); + /// let len = socket.peek(&mut buf).expect("`UnixDatagram::peek` should not fail"); /// Ok(()) /// } /// ``` diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index b7f8d25a85ae5..793e2378e716d 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -200,7 +200,7 @@ impl UnixListener { /// /// fn main() -> std::io::Result<()> { /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// let listener_copy = listener.try_clone().expect("`UnixListener::try_clone` should not fail"); /// Ok(()) /// } /// ``` @@ -219,7 +219,7 @@ impl UnixListener { /// /// fn main() -> std::io::Result<()> { /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// let addr = listener.local_addr().expect("`UnixListener::local_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -244,7 +244,7 @@ impl UnixListener { /// /// fn main() -> std::io::Result<()> { /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// listener.set_nonblocking(true).expect("`UnixListener::set_nonblocking` should not fail"); /// Ok(()) /// } /// ``` diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 31a908dfe187e..dba49d0406bf1 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -199,7 +199,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// let sock_copy = socket.try_clone().expect("`UnixStream::try_clone` should not fail"); /// Ok(()) /// } /// ``` @@ -218,7 +218,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixStream::local_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -237,7 +237,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// let addr = socket.peer_addr().expect("`UnixStream::peer_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -257,7 +257,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let peer_cred = socket.peer_cred().expect("Couldn't get peer credentials"); + /// let peer_cred = socket.peer_cred().expect("`UnixStream::peer_cred` should not fail"); /// Ok(()) /// } /// ``` @@ -295,7 +295,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("`UnixStream::set_read_timeout` should not fail"); /// Ok(()) /// } /// ``` @@ -340,7 +340,7 @@ impl UnixStream { /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; /// socket.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("Couldn't set write timeout"); + /// .expect("`UnixStream::connect` should not fail"); /// Ok(()) /// } /// ``` @@ -378,7 +378,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("`UnixStream::set_read_timeout` should not fail"); /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -400,7 +400,7 @@ impl UnixStream { /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; /// socket.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("Couldn't set write timeout"); + /// .expect("`UnixStream::set_write_timeout` should not fail"); /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -420,7 +420,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// socket.set_nonblocking(true).expect("`UnixStream::set_nonblocking` should not fail"); /// Ok(()) /// } /// ``` @@ -493,7 +493,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// socket.shutdown(Shutdown::Both).expect("`UnixStream::shutdown` should not fail"); /// Ok(()) /// } /// ``` @@ -520,7 +520,7 @@ impl UnixStream { /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; /// let mut buf = [0; 10]; - /// let len = socket.peek(&mut buf).expect("peek failed"); + /// let len = socket.peek(&mut buf).expect("`UnixStream::peek` should not fail"); /// Ok(()) /// } /// ``` diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 18897fbb5b8f0..0f5adb8b983e2 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -4,10 +4,9 @@ use clippy_utils::sugg::Sugg; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; -use rustc_hir_typeck::cast::check_cast; +use rustc_hir_typeck::cast::{CastKind, check_cast}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_middle::ty::cast::CastKind; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 166d45491242c..8cffd4defac3d 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -1020,8 +1020,10 @@ dependencies = [ "serde", "serde_json", "smallvec", + "socket2", "tempfile", "ui_test", + "windows-sys", ] [[package]] @@ -1484,6 +1486,16 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spanned" version = "0.4.1" diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index de4687715c06b..1c52d35bf5bb2 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -28,6 +28,7 @@ directories = "6" bitflags = "2.6" serde_json = { version = "1.0", optional = true } mio = { version = "1.1.1", features = ["os-poll", "net"] } +socket2 = "0.6.5" [target.'cfg(unix)'.dependencies] libc = "0.2" @@ -47,6 +48,11 @@ capstone = { version = "0.14", features = ["arch_x86", "full"], default-features [target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies] genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true } +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.61", features = [ + "Win32_Storage_FileSystem", +] } + [dev-dependencies] ui_test = "0.30.2" colored = "3" diff --git a/src/tools/miri/etc/rust_analyzer_helix.toml b/src/tools/miri/etc/rust_analyzer_helix.toml index 1015b4baa6b09..3b31ec68774d6 100644 --- a/src/tools/miri/etc/rust_analyzer_helix.toml +++ b/src/tools/miri/etc/rust_analyzer_helix.toml @@ -17,6 +17,10 @@ overrideCommand = [ "--message-format=json", ] +[language-server.rust-analyzer.config.diagnostics] +# RA reports false errors due to +enable = false + [language-server.rust-analyzer.config.cargo.extraEnv] MIRI_AUTO_OPS = "no" MIRI_IN_RA = "1" diff --git a/src/tools/miri/etc/rust_analyzer_zed.json b/src/tools/miri/etc/rust_analyzer_zed.json index e5ff400e989e7..f2debc2000acb 100644 --- a/src/tools/miri/etc/rust_analyzer_zed.json +++ b/src/tools/miri/etc/rust_analyzer_zed.json @@ -19,6 +19,10 @@ "--message-format=json" ] }, + "diagnostics": { + // RA reports false errors due to + "enable": false + }, "cargo": { "extraEnv": { "MIRI_AUTO_OPS": "no", diff --git a/src/tools/miri/priroda/Cargo.lock b/src/tools/miri/priroda/Cargo.lock index 8d4272630e08a..a690264e158fc 100644 --- a/src/tools/miri/priroda/Cargo.lock +++ b/src/tools/miri/priroda/Cargo.lock @@ -688,6 +688,8 @@ dependencies = [ "rand 0.10.2", "serde", "smallvec", + "socket2", + "windows-sys", ] [[package]] @@ -1115,6 +1117,16 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spanned" version = "0.4.1" diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 89c3e1334a546..eb8c4fb20edfe 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -88638df1e741778019e42fcdf307072865d19501 +5c543b0b8c73c7b72bc8284ced4fb22ead15734d diff --git a/src/tools/miri/src/intrinsics/x86/sse2.rs b/src/tools/miri/src/intrinsics/x86/sse2.rs index b39bfcaedea59..d9ef809598765 100644 --- a/src/tools/miri/src/intrinsics/x86/sse2.rs +++ b/src/tools/miri/src/intrinsics/x86/sse2.rs @@ -1,4 +1,5 @@ use rustc_apfloat::ieee::Double; +use rustc_middle::mir; use rustc_span::Symbol; use super::{ @@ -272,6 +273,41 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pmaddwd(this, left, right, dest)?; } + // Used to implement the _mm_mulhi_epi16 and _mm_mulhi_epu16 functions. + "pmulh.w" | "pmulhu.w" => { + let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let left = this.read_immediate(&this.project_index(&left, i)?)?; + let right = this.read_immediate(&this.project_index(&right, i)?)?; + let dest = this.project_index(&dest, i)?; + + // Widen the operands to avoid overflow + let twice_wide = this.layout_of(this.get_twice_wide_int_ty(left.layout.ty))?; + let left = this.int_to_int_or_float(&left, twice_wide)?; + let right = this.int_to_int_or_float(&right, twice_wide)?; + + // Multiply + let multiplied = this.binary_op(mir::BinOp::Mul, &left, &right)?; + // Keep the high half + let high = this.binary_op( + mir::BinOp::Shr, + &multiplied, + &ImmTy::from_uint(dest.layout.size.bits(), twice_wide), + )?; + + // Narrow back to the original type + let res = this.int_to_int_or_float(&high, dest.layout)?; + this.write_immediate(*res, &dest)?; + } + } _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index fa025f839057f..65eb6e10d4518 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -14,11 +14,11 @@ use rustc_abi::{Align, ExternAbi, Size}; use rustc_apfloat::{Float, FloatConvert}; use rustc_ast::Mutability; use rustc_ast::expand::allocator::{self, SpecialAllocatorMethod}; +use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; -use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_hir::def::DefKind; use rustc_log::tracing; use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind; diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index cd7a7f4f0250b..d842f2f69b713 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -513,7 +513,24 @@ impl FileDescription for FileHandle { #[derive(Debug)] pub struct DirHandle { - pub(crate) dir: Dir, + pub(super) dir: Dir, + #[cfg(bootstrap)] + pub(super) fallback: std::path::PathBuf, + #[cfg(not(bootstrap))] + #[expect(unused)] + fallback: (), +} + +impl DirHandle { + pub fn open(path: &std::path::Path) -> io::Result { + #[cfg(bootstrap)] + let fallback = path.canonicalize()?; + #[cfg(not(bootstrap))] + let fallback = (); + + let dir = Dir::open(path)?; + Ok(DirHandle { dir, fallback }) + } } impl FileDescription for DirHandle { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index cef93e9653430..66d65ead7c7bd 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -4,8 +4,8 @@ use std::path::Path; use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE; -use rustc_data_structures::either::Either; use rustc_attr_ir::Linkage; +use rustc_data_structures::either::Either; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -170,8 +170,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // picked by the linker. // Make sure we are consistent wrt what is 'first' and 'second'. - let original_span = - tcx.def_span(original.instance.def_id()); + let original_span = tcx.def_span(original.instance.def_id()); let span = tcx.def_span(def_id); if original_span.lo_hi() < span.lo_hi() { throw_machine_stop!( diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index f78c92de97bcc..54639db6e19cf 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -404,6 +404,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } + "fstatat" => { + let [dirfd, path, buf, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, *_, i32) -> i32), + (link_name, abi, args), + )?; + let result = this.fstatat(dirfd, path, buf, flags)?; + this.write_scalar(result, dest)?; + } "chmod" => { let [path, mode] = this.check_shim_sig( shim_sig!(extern "C" fn(*_, libc::mode_t) -> i32), diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 98a7c2ff3c2cf..1b9133785ed4a 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::ffi::OsString; -use std::fs::{self, Dir, DirBuilder, File, FileTimes, FileType, OpenOptions, TryLockError}; +use std::fs::{self, DirBuilder, File, FileTimes, FileType, OpenOptions, TryLockError}; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use std::path::{self, Path}; use std::time::SystemTime; @@ -20,14 +20,14 @@ use crate::shims::sig::Varargs; use crate::shims::unix::fd::{EvalContextExt as _, FlockOp, UnixFileDescription}; use crate::*; -/// An open directory stream, tracked by DirTable. +/// An open directory stream (`DIR`), tracked by DirTable. #[derive(Debug)] struct DirStream { - /// The directory reader on the host. + /// The directory stream on the host. read_dir: fs::ReadDir, - /// An FD number for the same handle. Unix directory streams have an "underlying FD" that - /// can be exposed; this is that FD. We also store the FD ID to catch cases where - /// the FD was closed and a different one re-opened. + /// Unix directory streams have an "underlying FD" that can be exposed; this is that FD. This is + /// not a reference; it *can* be separately closed, leading to UB if the DIR is used again. We + /// also store the FD ID to catch cases where the FD was closed and a different one re-opened. fd_num: FdNum, fd_id: FdId, /// The "special" entries that must still be yielded by the iterator. @@ -387,15 +387,17 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { }) } - #[cfg(unix)] fn host_permissions_from_mode(&self, mode: u32) -> InterpResult<'tcx, fs::Permissions> { - use std::os::unix::fs::PermissionsExt; - interp_ok(fs::Permissions::from_mode(mode)) - } - - #[cfg(not(unix))] - fn host_permissions_from_mode(&self, _mode: u32) -> InterpResult<'tcx, fs::Permissions> { - throw_unsup_format!("setting file permissions is only supported on Unix hosts") + cfg_select! { + unix => { + use std::os::unix::fs::PermissionsExt; + interp_ok(fs::Permissions::from_mode(mode)) + } + _ => { + let _ = mode; + throw_unsup_format!("setting file permissions is only supported on Unix hosts") + } + } } } @@ -481,20 +483,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; let mode = this.read_scalar(mode)?.to_u32()?; - #[cfg(unix)] - { - // Support all modes on UNIX host - use std::os::unix::fs::OpenOptionsExt; - options.mode(mode); - } - #[cfg(not(unix))] - { - // Only support default mode for non-UNIX (i.e. Windows) host - if mode != 0o666 { - throw_unsup_format!( - "non-default mode 0o{:o} is not supported on non-Unix hosts", - mode - ); + cfg_select! { + unix => { + // Support all modes on UNIX host + use std::os::unix::fs::OpenOptionsExt; + options.mode(mode); + } + _ => { + // Only support default mode for non-UNIX (i.e. Windows) host + if mode != 0o666 { + throw_unsup_format!( + "non-default mode 0o{:o} is not supported on non-Unix hosts", + mode + ); + } } } @@ -521,22 +523,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let o_nofollow = this.eval_libc_i32("O_NOFOLLOW"); + let mut nofollow = false; if flag & o_nofollow == o_nofollow { flag &= !o_nofollow; - #[cfg(unix)] - { - use std::os::unix::fs::OpenOptionsExt; - options.custom_flags(libc::O_NOFOLLOW); - } - // Strictly speaking, this emulation is not equivalent to the O_NOFOLLOW flag behavior: - // the path could change between us checking it here and the later call to `open`. - // But it's good enough for Miri purposes. - #[cfg(not(unix))] - { - // O_NOFOLLOW only fails when the trailing component is a symlink; - // the entire rest of the path can still contain symlinks. - if path.is_symlink() { - return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); + nofollow = true; + cfg_select! { + unix => { + use std::os::unix::fs::OpenOptionsExt; + options.custom_flags(libc::O_NOFOLLOW); + } + windows => { + use std::os::windows::fs::OpenOptionsExt; + options.custom_flags( + windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT, + ); } } } @@ -552,11 +552,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_errno_and_return_neg1_i32(ErrorKind::PermissionDenied); } - let fd = options - .open(path) - .map(|file| this.machine.fds.insert_new(FileHandle { file, writable, readable })); - - interp_ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) + let file = match options.open(path) { + Ok(file) => file, + Err(err) => return this.set_errno_and_return_neg1_i32(err), + }; + let metadata = file.metadata().expect("a just-opened file should have metadata"); + if metadata.is_dir() { + throw_unsup_format!("open: opening directories is not supported"); + } + if nofollow && !cfg!(unix) { + // On Windows, FILE_FLAG_OPEN_REPARSE_POINT makes opening still succeed, it just + // opens the symlink rather than the target. Turn that into an error. + if metadata.is_symlink() { + return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); + } + } + let fd = this.machine.fds.insert_new(FileHandle { file, writable, readable }); + interp_ok(Scalar::from_i32(fd)) } fn lseek( @@ -618,15 +630,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { target_op: &OpTy<'tcx>, linkpath_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> { - #[cfg(unix)] fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { - std::os::unix::fs::symlink(src, dst) - } - - #[cfg(windows)] - fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { - use std::os::windows::fs; - if src.is_dir() { fs::symlink_dir(src, dst) } else { fs::symlink_file(src, dst) } + cfg_select! { + unix => std::os::unix::fs::symlink(src, dst), + windows => { + use std::os::windows::fs; + // This is racy, but not much we can do about that. + if src.is_dir() { + fs::symlink_dir(src, dst) + } else { + fs::symlink_file(src, dst) + } + } + } } let this = self.eval_context_mut(); @@ -766,7 +782,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fstat`", reject_with)?; - // Set error code as "EBADF" (bad fd) + // Set error code as "EBADF" (bad fd); "EACCESS" does not make much sense here. return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); } @@ -777,6 +793,83 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) } + fn fstatat( + &mut self, + dirfd_op: &OpTy<'tcx>, + path_op: &OpTy<'tcx>, + buf_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + if !matches!( + &this.tcx.sess.target.os, + Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux | Os::Android + ) { + panic!("`fstatat` should not be called on {}", this.tcx.sess.target.os); + } + + let dirfd = this.read_scalar(dirfd_op)?.to_i32()?; + let path = this.read_pointer(path_op)?; + let flags = this.read_scalar(flags_op)?.to_i32()?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fstatat`", reject_with)?; + return this.set_errno_and_return_neg1_i32(LibcError("EACCES")); + } + + // Parse flags. + let mut flags = flags; + // AT_SYMLINK_NOFOLLOW + let at_symlink_nofollow = this.eval_libc_i32("AT_SYMLINK_NOFOLLOW"); + let symlink_nofollow_flag = flags & at_symlink_nofollow == at_symlink_nofollow; + flags &= !at_symlink_nofollow; + // Complain about unknown flags. + if flags != 0 { + throw_unsup_format!("unsupported flags for `fstatat`: {flags:#x}") + } + + let path = this.read_path_from_c_str(path)?.into_owned(); + let metadata = if path.is_empty() { + throw_unsup_format!("fstatat: empty path is not supported"); + } else if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD") { + // Either absolute path (dirfd is ignored) or relative to working directory. + FileMetadata::from_host( + this, + if symlink_nofollow_flag { path.symlink_metadata() } else { path.metadata() }, + )? + } else { + // relative to dirfd, which must be a directory handle + let Some(fd) = this.machine.fds.get(dirfd) else { + return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); + }; + let Some(dir) = fd.downcast::() else { + return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); + }; + + #[cfg(not(bootstrap))] + let metadata = if symlink_nofollow_flag { + dir.dir.symlink_metadata(path) + } else { + dir.dir.metadata(path) + }; + #[cfg(bootstrap)] + let metadata = if symlink_nofollow_flag { + dir.fallback.join(path).symlink_metadata() + } else { + dir.fallback.join(path).metadata() + }; + FileMetadata::from_host(this, metadata)? + }; + + let metadata = match metadata { + Ok(metadata) => metadata, + Err(err) => return this.set_errno_and_return_neg1_i32(err), + }; + interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) + } + fn linux_statx( &mut self, dirfd_op: &OpTy<'tcx>, // Should be an `int` @@ -836,18 +929,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Either absolute path (dirfd is ignored) or relative to working directory. FileMetadata::from_host( this, - if symlink_nofollow_flag { fs::symlink_metadata(path) } else { fs::metadata(path) }, + if symlink_nofollow_flag { path.symlink_metadata() } else { path.metadata() }, )? } else { // relative to dirfd, which must be a directory handle let Some(fd) = this.machine.fds.get(dirfd) else { return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); }; - let Some(_dir) = fd.downcast::() else { + let Some(dir) = fd.downcast::() else { return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); }; - throw_unsup_format!("statx relative to a directory handle is not supported"); + #[cfg(not(bootstrap))] + let metadata = if symlink_nofollow_flag { + dir.dir.symlink_metadata(path) + } else { + dir.dir.metadata(path) + }; + #[cfg(bootstrap)] + let metadata = if symlink_nofollow_flag { + dir.fallback.join(path).symlink_metadata() + } else { + dir.fallback.join(path).metadata() + }; + FileMetadata::from_host(this, metadata)? }; let metadata = match metadata { @@ -1117,12 +1222,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // in between above and here. One day, the standard library will support converting // between `Dir` and `ReadDir` (one of the two directions would suffice for our // needs), then we'll use that. - let Ok(dir) = Dir::open(name) else { + let Ok(dir) = DirHandle::open(&name) else { throw_unsup_format!( "cannot `opendir` this directory: failed to create directory handle" ); }; - let dir = this.machine.fds.new_ref(DirHandle { dir }); + let dir = this.machine.fds.new_ref(dir); let dir_fd_id = dir.id(); let dir_fd_num = this.machine.fds.insert(dir); @@ -1859,9 +1964,6 @@ fn extract_sec_and_nsec<'tcx>( } fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str { - #[cfg(unix)] - use std::os::unix::fs::FileTypeExt; - if file_type.is_file() { "S_IFREG" } else if file_type.is_dir() { @@ -1872,6 +1974,7 @@ fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str { // Certain file types are only available when the host is a Unix system. #[cfg(unix)] { + use std::os::unix::fs::FileTypeExt; if file_type.is_socket() { return "S_IFSOCK"; } else if file_type.is_fifo() { diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index b33de1b713fea..25ff4fe43b659 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -65,6 +65,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.fstat(fd, buf)?; this.write_scalar(result, dest)?; } + "fstatat$INODE64" => { + let [dirfd, path, buf, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, *_, i32) -> i32), + (link_name, abi, args), + )?; + let result = this.fstatat(dirfd, path, buf, flags)?; + this.write_scalar(result, dest)?; + } "opendir$INODE64" => { let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.opendir(name)?; diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 8c442df216a47..ce1f5c06b6b2c 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -10,14 +10,6 @@ use crate::shims::unix::socket_address::EvalContextExt as _; use crate::shims::unix::tcp_socket::TcpSocket; use crate::*; -#[derive(Debug, PartialEq)] -pub enum SocketFamily { - // IPv4 internet protocols - IPv4, - // IPv6 internet protocols - IPv6, -} - /// Represents unix-specific socket file descriptions. /// /// Not to be confused with Unix domain sockets. @@ -204,9 +196,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let family = if domain == this.eval_libc_i32("AF_INET") { - SocketFamily::IPv4 + socket2::Domain::IPV4 } else if domain == this.eval_libc_i32("AF_INET6") { - SocketFamily::IPv6 + socket2::Domain::IPV6 } else { throw_unsup_format!( "socket: domain {:#x} is unsupported, only AF_INET and \ @@ -229,8 +221,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } + let socket = match TcpSocket::new(family, is_non_block) { + Ok(socket) => socket, + Err(e) => return this.set_errno_and_return_neg1_i32(e), + }; let fds = &mut this.machine.fds; - let fd = fds.new_ref(TcpSocket::new(family, is_non_block)); + let fd = fds.new_ref(socket); interp_ok(Scalar::from_i32(fds.insert(fd))) } diff --git a/src/tools/miri/src/shims/unix/tcp_socket.rs b/src/tools/miri/src/shims/unix/tcp_socket.rs index b59174cd71c2d..394f80a151f06 100644 --- a/src/tools/miri/src/shims/unix/tcp_socket.rs +++ b/src/tools/miri/src/shims/unix/tcp_socket.rs @@ -1,9 +1,9 @@ use std::cell::{Cell, RefCell, RefMut}; -use std::io; use std::io::Read; -use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4}; +use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::sync::atomic::AtomicBool; use std::time::Duration; +use std::{io, mem}; use mio::event::Source; use mio::net::{TcpListener, TcpStream}; @@ -14,7 +14,7 @@ use rustc_target::spec::Os; use crate::shims::files::{EvalContextExt as _, FdNum, FileDescription, FileDescriptionRef}; use crate::shims::sig::Varargs; use crate::shims::unix::UnixFileDescription; -use crate::shims::unix::socket::{SocketFamily, UnixSocketFileDescription}; +use crate::shims::unix::socket::UnixSocketFileDescription; use crate::*; /// On Linux a TCP socket is initally in the TCP_CLOSE state: @@ -35,12 +35,10 @@ const INITIAL_TCP_SOCKET_READINESS: Readiness = #[derive(Debug)] enum SocketState { /// No syscall after `socket` has been made. - Initial, - /// The `bind` syscall has been called on the socket. - /// This is only reachable from the [`SocketState::Initial`] state. - Bound(SocketAddr), + Initial(socket2::Socket), /// The `listen` syscall has been called on the socket. - /// This is only reachable from the [`SocketState::Bound`] state. + /// This is reachable from the [`SocketState::Initial`] and the + /// [`SocketState::Listening`] states. Listening(TcpListener), /// The `connect` syscall has been called and we weren't yet able /// to ensure the connection is established. This is only reachable @@ -59,17 +57,45 @@ enum SocketState { /// and thus nothing (except destroying the socket) should be /// supported when a socket is in this state. ConnectionFailed(TcpStream), + /// The socket state machine is transitioning between two states + /// which contain an underlying host socket. This state is never + /// observable, it's only needed as a placeholder state to safely + /// acquire ownership of the underlying host socket. + // FIXME: This would not be needed with `replace_with`, but sadly + // this isn't part of the standard library and we don't want to add + // an extra dependency for this. + None, +} + +impl SocketState { + /// View the underlying host socket as a [`socket2::SockRef`]. + /// + /// **Note**: Potentially blocking operations need to be performed on the + /// underlying [`TcpStream`] and [`TcpListener`] as it would break the mio + /// poll on Windows hosts when performed directly on the [`socket2::SockRef`]. + fn as_socket_ref<'a>(&'a self) -> socket2::SockRef<'a> { + match self { + SocketState::Initial(socket) => socket.into(), + SocketState::Listening(listener) => listener.into(), + SocketState::Connecting(stream) + | SocketState::Connected(stream) + | SocketState::ConnectionFailed(stream) => stream.into(), + SocketState::None => unreachable!(), + } + } } #[derive(Debug)] pub(super) struct TcpSocket { /// Family of the socket, used to ensure socket only binds/connects to address of /// same family. - family: SocketFamily, + family: socket2::Domain, /// Current state of the inner socket. state: RefCell, /// Whether this fd is non-blocking or not. is_non_block: Cell, + /// Whether the socket is implicitly or explicitly bound to an address. + is_bound: Cell, /// The current blocking I/O readiness of the file description. io_readiness: RefCell, /// [`Some`] when the socket had an async error which has not yet been fetched via `SO_ERROR`. @@ -91,17 +117,41 @@ pub(super) struct TcpSocket { } impl TcpSocket { - pub fn new(family: SocketFamily, is_non_block: bool) -> Self { - TcpSocket { + pub fn new(family: socket2::Domain, is_non_block: bool) -> io::Result { + let socket = + socket2::Socket::new(family, socket2::Type::STREAM, Some(socket2::Protocol::TCP))?; + // The underlying host socket needs to be non-blocking. The actual + // blocking mode of the socket is stored in `is_non_block`. + socket.set_nonblocking(true)?; + + // We don't add the newly created socket to the blocking I/O manager + // until we can create a mio TcpStream or TcpListener because + // `socket2::Socket` doesn't implement `mio::Source`. + // This is fine since the readiness of a TCP socket (at least under Linux) + // does have a static initial readiness until `connect` or `listen` is + // invoked on it. + + Ok(TcpSocket { family, - state: RefCell::new(SocketState::Initial), + state: RefCell::new(SocketState::Initial(socket)), is_non_block: Cell::new(is_non_block), + is_bound: Cell::new(false), io_readiness: RefCell::new(INITIAL_TCP_SOCKET_READINESS), error: RefCell::new(None), read_timeout: Cell::new(None), write_timeout: Cell::new(None), watched: ReadinessWatched::default(), - } + }) + } + + /// Invoke `cb` with a [`socket2::SockRef`] to the underlying host socket. + /// + /// **Note**: Potentially blocking operations need to be performed on the + /// underlying [`TcpStream`] and [`TcpListener`] as it would break the mio + /// poll on Windows hosts when performed directly on the [`socket2::SockRef`]. + fn with_socket_ref(&self, cb: impl FnOnce(socket2::SockRef<'_>) -> T) -> T { + let state = self.state.borrow(); + cb(state.as_socket_ref()) } } @@ -251,54 +301,45 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "bind")?; - let mut state = self.state.borrow_mut(); + let address_family = match &address { + SocketAddr::V4(_) => socket2::Domain::IPV4, + SocketAddr::V6(_) => socket2::Domain::IPV6, + }; - match *state { - SocketState::Initial => { - let address_family = match &address { - SocketAddr::V4(_) => SocketFamily::IPv4, - SocketAddr::V6(_) => SocketFamily::IPv6, - }; + if self.family != address_family { + // Attempted to bind an address from a family that doesn't match + // the family of the socket. + let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android) { + // Linux man page states that `EINVAL` is used when there is an address family mismatch. + // See + LibcError("EINVAL") + } else { + // POSIX man page states that `EAFNOSUPPORT` should be used when there is an address + // family mismatch. + // See + LibcError("EAFNOSUPPORT") + }; + return interp_ok(Err(err)); + } - if self.family != address_family { - // Attempted to bind an address from a family that doesn't match - // the family of the socket. - let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android) { - // Linux man page states that `EINVAL` is used when there is an address family mismatch. - // See - LibcError("EINVAL") - } else { - // POSIX man page states that `EAFNOSUPPORT` should be used when there is an address - // family mismatch. - // See - LibcError("EAFNOSUPPORT") - }; - return interp_ok(Err(err)); - } + let state = self.state.borrow(); + let socket = state.as_socket_ref(); - *state = SocketState::Bound(address); - } - SocketState::Connecting(_) | SocketState::Connected(_) => - throw_unsup_format!( - "bind: tcp socket is already connected and binding a - connected socket is unsupported" - ), - SocketState::Bound(_) | SocketState::Listening(_) => - throw_unsup_format!( - "bind: tcp socket is already bound and binding a socket \ - multiple times is unsupported" - ), - SocketState::ConnectionFailed(_) => unreachable!(), + // `bind` is a non-blocking operation for TCP sockets. + if let Err(e) = socket.bind(&socket2::SockAddr::from(address)) { + return interp_ok(Err(IoError::HostError(e))); } + // The socket has been explicitly bound to a local address. + self.is_bound.set(true); + interp_ok(Ok(())) } fn listen<'tcx>( self: FileDescriptionRef, communicate_allowed: bool, - // Since the backlog value is just a performance hint we can ignore it. - _backlog: i32, + backlog: i32, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, Result<(), IoError>> { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); @@ -306,43 +347,111 @@ impl UnixSocketFileDescription for TcpSocket { let mut state = self.state.borrow_mut(); - match *state { - SocketState::Bound(socket_addr) => - match TcpListener::bind(socket_addr) { - Ok(listener) => { - *state = SocketState::Listening(listener); - drop(state); - - // After invoking `listen` on a TCP socket, it transitions out of the - // TCP_CLOSE state which affects the socket's readiness. Because we - // register the socket afterwards to the blocking I/O manager, we just - // clear its readiness here as the blocking I/O manager will update its - // readiness accordingly. - self.io_readiness.replace(Readiness::EMPTY); - ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; - - // Register the socket to the blocking I/O manager because - // we now have an associated host socket. - ecx.machine.blocking_io.register(self); - } - Err(e) => return interp_ok(Err(IoError::HostError(e))), - }, - SocketState::Initial => { - throw_unsup_format!( - "listen: listening on a tcp socket which isn't bound is unsupported" - ) - } - SocketState::Listening(_) => { - throw_unsup_format!( - "listen: listening on a tcp socket multiple times is unsupported" - ) - } - SocketState::Connecting(_) | SocketState::Connected(_) => { - throw_unsup_format!("listen: listening on a connected tcp socket is unsupported") + // We'll only attempt to invoke `listen` on the socket if it's not already + // connected because POSIX specifies that EINVAL should be returned in this + // case. + let socket = match &*state { + SocketState::Initial(_) | SocketState::Listening(_) => state.as_socket_ref(), + // POSIX states that connected sockets cannot start listening, + // so we can return early here to simplify the remaining shim. + SocketState::Connecting(_) | SocketState::Connected(_) => + return interp_ok(Err(LibcError("EINVAL"))), + SocketState::ConnectionFailed(_) | SocketState::None => unreachable!(), + }; + + // Despite not being specified by POSIX, on most of our supported Unix-like platforms + // (confirmed on Linux, macOS, FreeBSD, and Solaris) TCP sockets get implicitly bound + // to the unspecified address at a random port when `listen` is invoked on an unbound + // socket. Thus, Miri models the same non-POSIX behavior and treats a socket as bound + // once `listen` has successfully been invoked on an unbound socket. + + if cfg!(windows) && !self.is_bound.get() { + // Implicitly binding a TCP socket by invoking `listen` on an + // unbound socket causes EINVAL on Windows hosts. We thus + // explicitly bind the socket to an unspecified address with + // a random port before listening. + // + // When the subsequent `listen` fails we behave incorrectly + // because a previously unbound socket is now bound. However, + // since `listen` can only fail for system resource errors + // (e.g., ENOBUFS) this isn't a problem in reality. + + let address = if self.family == socket2::Domain::IPV4 { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, /* port */ 0)) + } else { + SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + /* port */ 0, + /* flowinfo */ 0, + /* scope_id */ 0, + )) + }; + + if let Err(e) = socket.bind(&socket2::SockAddr::from(address)) { + return interp_ok(Err(IoError::HostError(e))); } - SocketState::ConnectionFailed(_) => unreachable!(), + + // We just explicitly bound the socket to a local address. + self.is_bound.set(true); } + if let Err(e) = socket.listen(backlog) { + return interp_ok(Err(IoError::HostError(e))); + } + + // As mentioned above, we treat listening sockets as implicitly bound to a + // local address if they weren't already bound before the `listen` invocation. + self.is_bound.set(true); + + if let SocketState::Listening(_) = &*state { + // The socket was already listening; we just changed the backlog. + // The socket is already registered to the blocking I/O manager + // and we also don't need to perform a state transition. + return interp_ok(Ok(())); + } + + // Temporarily use dummy state to take ownership of the underlying socket. + let SocketState::Initial(socket) = mem::replace(&mut *state, SocketState::None) else { + unreachable!() + }; + + // FIXME: Rustfmt has a bug where it incorrectly removes the outer braces of {{ .. }} inside + // a `cfg_select!` block. See . + #[rustfmt::skip] + let listener = cfg_select! { + // Turn the `socket2::Socket` into a `mio::TcpListener`. + // We follow Tokio's TCP socket implementation: + // See + // SAFETY: mio specifies that it is safe to use `from_raw_fd` and + // `from_raw_socket` as long as it's ensured that the socket is non-blocking. + // Because we immediately make the socket non-blocking after creation, that + // is always guaranteed. + + unix => {{ + use std::os::fd::{IntoRawFd, FromRawFd}; + let raw_fd = socket.into_raw_fd(); + unsafe { TcpListener::from_raw_fd(raw_fd) } + }}, + windows => {{ + use std::os::windows::io::{IntoRawSocket, FromRawSocket}; + let raw_socket = socket.into_raw_socket(); + unsafe { TcpListener::from_raw_socket(raw_socket) } + }}, + _ => unreachable!("unsupported host platform") + }; + + *state = SocketState::Listening(listener); + drop(state); + + // Clear the "artificial" readiness as we are about to put the blocking + // I/O manager in charge of handling readiness. + self.io_readiness.replace(Readiness::EMPTY); + ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; + + // Register the socket to the blocking I/O manager. Can only happen + // now that we have created a mio TcpListener. + ecx.machine.blocking_io.register(self); + interp_ok(Ok(())) } @@ -394,52 +503,110 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "connect")?; - match &*self.state.borrow() { - SocketState::Initial => { /* fall-through to below */ } + let mut state = self.state.borrow_mut(); + + // We'll only attempt to invoke `connect` on the socket if it's not already + // connected or listening because POSIX specifies error codes which should + // be returned in those cases. + let socket = match &*state { + SocketState::Initial(socket) => socket, + SocketState::Listening(_) => { + let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android) { + // Linux-like targets return EISCONN when attempting to invoke + // `connect` on an already listening socket. + // See + LibcError("EISCONN") + } else { + // POSIX specifies to return EOPNOTSUPP when attempting to invoke + // `connect` on an already listening socket. + // See + LibcError("EOPNOTSUPP") + }; + return finish.call(ecx, Err(err)); + } + // The socket is already connected. + // Subsequent connection attempts return EISCONN for TCP sockets. + SocketState::Connected(_) => return finish.call(ecx, Err(LibcError("EISCONN"))), // The socket is already in a connecting state. + // Subsequent connection attempts return EALREADY for TCP sockets. SocketState::Connecting(_) => return finish.call(ecx, Err(LibcError("EALREADY"))), - // We don't return EISCONN for already connected sockets, for which we're - // sure that the connection is established, since TCP sockets are usually - // allowed to be connected multiple times. - _ => - throw_unsup_format!( - "connect: connecting is only supported for tcp sockets which are neither \ - bound, listening nor already connected" - ), + SocketState::ConnectionFailed(_) | SocketState::None => unreachable!(), + }; + + let result = socket.connect(&socket2::SockAddr::from(address)); + + // Boolean whether the connection attempt "failed" because it could not be + // completed immediately without blocking. + let is_in_progress = result.as_ref().is_err_and(|e| { + // On Windows hosts non-blocking connects fail with EWOULDBLOCK when the connection cannot + // be established immediately, while on Unix-like hosts they fail with EINPROGRESS. + matches!(e.kind(), io::ErrorKind::WouldBlock | io::ErrorKind::InProgress) + }); + + if !is_in_progress && let Err(e) = result { + // There was a "real" error during connection establishment. + return finish.call(ecx, Err(IoError::HostError(e))); } - // This begins establishing the connection, but does not block until the stream is fully connected. - // We deal with that below. - match TcpStream::connect(address) { - Ok(stream) => { - *self.state.borrow_mut() = SocketState::Connecting(stream); - - // After invoking `connect` on a TCP socket, it transitions out of the - // TCP_CLOSE state which affects the socket's readiness. Because we - // register the socket afterwards to the blocking I/O manager, we just - // clear its readiness here as the blocking I/O manager will update its - // readiness accordingly. - self.io_readiness.replace(Readiness::EMPTY); - ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; - - // Register the socket to the blocking I/O manager because - // we now have an associated host socket. - ecx.machine.blocking_io.register(self.clone()); - } - Err(e) => return finish.call(ecx, Err(IoError::HostError(e))), + // Temporarily use dummy state to take ownership of the underlying socket. + let SocketState::Initial(socket) = mem::replace(&mut *state, SocketState::None) else { + unreachable!() }; + // FIXME: Rustfmt has a bug where it incorrectly removes the outer braces of {{ .. }} inside + // a `cfg_select!` block. See . + #[rustfmt::skip] + let stream = cfg_select! { + // Turn the `socket2::Socket` into a `mio::TcpStream`. + // We follow Tokio's TCP socket implementation: + // See + // SAFETY: mio specifies that it is safe to use `from_raw_fd` and + // `from_raw_socket` as long as it's ensured that the socket is non-blocking. + // Because we immediately make the socket non-blocking after creation, that + // is always guaranteed. + + unix => {{ + use std::os::fd::{IntoRawFd, FromRawFd}; + let raw_fd = socket.into_raw_fd(); + unsafe { TcpStream::from_raw_fd(raw_fd) } + }}, + windows => {{ + use std::os::windows::io::{IntoRawSocket, FromRawSocket}; + let raw_socket = socket.into_raw_socket(); + unsafe { TcpStream::from_raw_socket(raw_socket) } + }}, + _ => unreachable!("unsupported host platform") + }; + + *state = SocketState::Connecting(stream); + drop(state); + + // Connecting sockets are implicitly bound to a local address. + self.is_bound.set(true); + + // Clear the "artificial" readiness as we are about to put the blocking + // I/O manager in charge of handling readiness. + self.io_readiness.replace(Readiness::EMPTY); + ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; + + // Register the socket to the blocking I/O manager. Can only happen + // now that we have created a mio TcpStream. + ecx.machine.blocking_io.register(self.clone()); + if self.is_non_block.get() { // We have a non-blocking socket and thus don't want to block until // the connection is established. - // Since the [`TcpStream::connect`] function of mio hides the EINPROGRESS - // we just always return EINPROGRESS and check whether the connection succeeded - // once we want to use the connected socket. - finish.call(ecx, Err(LibcError("EINPROGRESS"))) + if is_in_progress { + finish.call(ecx, Err(LibcError("EINPROGRESS"))) + } else { + finish.call(ecx, Ok(())) + } } else { // The socket is in blocking mode and thus the connect call should block - // until the connection with the server is established. + // until the connection with the server is established. A potential + // EWOULDBLOCK or EINPROGRESS error code is ignored as we emulate a + // blocking socket. if self.write_timeout.get().is_some() { // Some Unixes like Linux also apply the SO_SNDTIMEO socket option @@ -647,19 +814,7 @@ impl UnixSocketFileDescription for TcpSocket { let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.u32); let ttl = ecx.read_scalar(&option_value)?.to_u32()?; - let result = match &*self.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 tcp sockets" - ), - SocketState::Listening(listener) => listener.set_ttl(ttl), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.set_ttl(ttl), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - return match result { + return match self.with_socket_ref(|s| s.set_ttl_v4(ttl)) { Ok(_) => interp_ok(Ok(())), Err(e) => interp_ok(Err(IoError::HostError(e))), }; @@ -679,18 +834,7 @@ impl UnixSocketFileDescription for TcpSocket { let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32); let nodelay = ecx.read_scalar(&option_value)?.to_i32()? != 0; - let result = match &*self.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 tcp sockets" - ), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.set_nodelay(nodelay), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - return match result { + return match self.with_socket_ref(|s| s.set_tcp_nodelay(nodelay)) { Ok(_) => interp_ok(Ok(())), Err(e) => interp_ok(Err(IoError::HostError(e))), }; @@ -772,19 +916,7 @@ impl UnixSocketFileDescription for TcpSocket { let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL"); if option == opt_ip_ttl { - let ttl = match &*self.state.borrow() { - SocketState::Initial | SocketState::Bound(_) => - throw_unsup_format!( - "getsockopt: reading option IP_TTL on level IPPROTO_IP is only supported \ - on connected and listening tcp sockets" - ), - SocketState::Listening(listener) => listener.ttl(), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.ttl(), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - let ttl = match ttl { + let ttl = match self.with_socket_ref(|s| s.ttl_v4()) { Ok(ttl) => ttl, Err(e) => return interp_ok(Err(IoError::HostError(e))), }; @@ -802,18 +934,7 @@ impl UnixSocketFileDescription for TcpSocket { let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY"); if option == opt_tcp_nodelay { - let nodelay = match &*self.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 tcp sockets" - ), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.nodelay(), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - let nodelay = match nodelay { + let nodelay = match self.with_socket_ref(|s| s.tcp_nodelay()) { Ok(nodelay) => nodelay, Err(e) => return interp_ok(Err(IoError::HostError(e))), }; @@ -843,52 +964,45 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "getsockname")?; - let state = self.state.borrow(); + if !self.is_bound.get() { + // Since Windows returns EINVAL when invoking `getsockname` on + // a socket which hasn't been bound yet, we need to manually + // return an unspecified address here. - let address = match &*state { - SocketState::Bound(address) => { - if address.port() == 0 { - // The socket is bound to a zero-port which means it gets assigned a random - // port. Since we don't yet have an underlying socket, we don't know what this - // random port will be and thus this is unsupported. - throw_unsup_format!( - "getsockname: when the port is 0, getting the tcp socket address before \ - calling `listen` or `connect` is unsupported" - ) - } + let address = if self.family == socket2::Domain::IPV4 { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, /* port */ 0)) + } else { + SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + /* port */ 0, + /* flowinfo */ 0, + /* scope_id */ 0, + )) + }; + return interp_ok(Ok(address)); + } - *address - } - SocketState::Listening(listener) => - match listener.local_addr() { - Ok(address) => address, - Err(e) => return interp_ok(Err(IoError::HostError(e))), - }, - SocketState::Connecting(stream) | SocketState::Connected(stream) => { - if cfg!(windows) && matches!(&*state, SocketState::Connecting(_)) { - // FIXME: On Windows hosts `TcpStream::local_addr` returns `0.0.0.0:0` whilst - // the socket is connecting: - // - // This is problematic because UNIX targets could expect a real local address even - // for a connecting non-blocking socket. - - static DEDUP: AtomicBool = AtomicBool::new(false); - if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { - ecx.emit_diagnostic(NonHaltingDiagnostic::ConnectingSocketGetsockname); - } - } - match stream.local_addr() { - Ok(address) => address, - Err(e) => return interp_ok(Err(IoError::HostError(e))), - } + let state = self.state.borrow(); + let socket = state.as_socket_ref(); + + if cfg!(windows) + && let SocketState::Connecting(_) = &*state + { + // FIXME: On Windows hosts `getsockname` returns `0.0.0.0:0` whilst the socket is connecting: + // + // This is problematic because UNIX targets could expect a real local address even + // for a connecting non-blocking socket. + + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { + ecx.emit_diagnostic(NonHaltingDiagnostic::ConnectingSocketGetsockname); } - // 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!(), - }; + } - interp_ok(Ok(address)) + match socket.local_addr() { + Ok(address) => interp_ok(Ok(address.as_socket().unwrap())), + Err(e) => interp_ok(Err(IoError::HostError(e))), + } } fn getpeername<'tcx>( @@ -1077,14 +1191,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let family = match addr { - SocketAddr::V4(_) => SocketFamily::IPv4, - SocketAddr::V6(_) => SocketFamily::IPv6, + SocketAddr::V4(_) => socket2::Domain::IPV4, + SocketAddr::V6(_) => socket2::Domain::IPV6, }; let fd = this.machine.fds.new_ref(TcpSocket { family, state: RefCell::new(SocketState::Connected(stream)), is_non_block: Cell::new(is_client_sock_nonblock), + // Connected sockets are implicitly bound to a local address. + is_bound: Cell::new(true), io_readiness: RefCell::new(Readiness::EMPTY), error: RefCell::new(None), read_timeout: Cell::new(None), @@ -1481,16 +1597,17 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // In other words, we are assuming that there will be no spurious // wakeups while establishing the connection. - // The connection is established. + // The connection is established. Update the state. + let mut state = socket.state.borrow_mut(); // 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 { + let SocketState::Connecting(stream) = mem::replace(&mut *state, SocketState::None) else { // At the start of the function we ensured that we're currently connecting. unreachable!() }; *state = SocketState::Connected(stream); drop(state); + action.call(this, Ok(())) } ), @@ -1525,14 +1642,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 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 new_error = + state.as_socket_ref().take_error().expect("Reading SO_ERROR should not fail"); let Some(new_error) = new_error else { return }; // Store the error such that we can return it when @@ -1546,8 +1657,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // 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) + let SocketState::Connecting(stream) = mem::replace(&mut *state, SocketState::None) else { unreachable!() }; diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index bab26d80dee70..6fc9d37070f67 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -1,7 +1,6 @@ -use std::fs::{self, Dir}; -use std::io; use std::io::SeekFrom; use std::time::SystemTime; +use std::{fs, io}; use bitflags::bitflags; use rustc_abi::Size; @@ -214,7 +213,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Open this as a directory. // FIXME: shouldn't we check `creation_disposition` here? We do know that it already // exists. - let dir = match Dir::open(&file_name) { + let dir = match DirHandle::open(&file_name) { Ok(dir) => dir, Err(e) => { if e.kind() == io::ErrorKind::NotADirectory { @@ -226,9 +225,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } }; #[cfg(bootstrap)] - let metadata = dir.metadata(); + let metadata = dir.dir.metadata(); #[cfg(not(bootstrap))] - let metadata = dir.self_metadata(); + let metadata = dir.dir.self_metadata(); if !metadata.unwrap().is_dir() { // This changed from a directory to a file. Retry. continue; @@ -239,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; } - let fd_num = this.machine.fds.insert_new(DirHandle { dir }); + let fd_num = this.machine.fds.insert_new(dir); return interp_ok(Handle::File(fd_num)); } else { // Per the documentation: @@ -381,7 +380,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let attributes = if file_type.is_dir() { this.eval_windows_u32("c", "FILE_ATTRIBUTE_DIRECTORY") } else if file_type.is_file() { - this.eval_windows_u32("c", "FILE_ATTRIBUTE_NORMAL") + // Normal files seem to have the "archive" attribute. There's also + // `FILE_ATTRIBUTE_NORMAL` but that's for files without any other attribute which, + // apparently, is not normal. + this.eval_windows_u32("c", "FILE_ATTRIBUTE_ARCHIVE") } else { this.eval_windows_u32("c", "FILE_ATTRIBUTE_DEVICE") }; diff --git a/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs b/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs index 7cab24bc4d4d9..b34a36babb2a9 100644 --- a/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs +++ b/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs @@ -1,9 +1,7 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation -use std::ffi::CString; use std::fs::create_dir; -use std::os::unix::ffi::OsStrExt; #[path = "../../utils/mod.rs"] mod utils; @@ -16,7 +14,7 @@ use libc_utils::errno_check; fn main() { let path = utils::prepare_dir("miri_test_libc_dirfd_closed"); create_dir(&path).expect("create_dir failed"); - let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let cpath = utils::into_c_string(path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); diff --git a/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs b/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs index e5e2336e96d63..dd6d404874401 100644 --- a/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs +++ b/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs @@ -1,9 +1,7 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation -use std::ffi::CString; use std::fs::create_dir; -use std::os::unix::ffi::OsStrExt; #[path = "../../utils/mod.rs"] mod utils; @@ -16,7 +14,7 @@ use libc_utils::{errno_check, errno_result}; fn main() { let path = utils::prepare_dir("miri_test_libc_dirfd_replaced"); create_dir(&path).expect("create_dir failed"); - let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let cpath = utils::into_c_string(path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs index 4400d52e59d6c..aa9cbe4298105 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs @@ -4,7 +4,6 @@ // Short FD ops can affect the exact error message here //@compile-flags: -Zmiri-disable-isolation -Zmiri-no-short-fd-operations -use std::ffi::CString; use std::fs::remove_file; use std::mem::MaybeUninit; @@ -16,8 +15,8 @@ mod libc_utils; fn main() { let path = - utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); + utils::prepare_with_content("miri-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); + let cpath = utils::into_c_string(&path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); diff --git a/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs b/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs index 3382670b50f18..39d2acffc0708 100644 --- a/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs +++ b/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs @@ -4,13 +4,10 @@ #![allow(invalid_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr}; -use std::os::unix::ffi::OsStrExt; - use libc::open; fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let _fd = unsafe { open(c_path.as_ptr(), libc::O_CREAT, /* should be mode_t */ 0u64) //~^ ERROR: /expected argument #3 to have type `(u32|i32)` but got incompatible type `u64`/ diff --git a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs index d355da73174e9..5fa07989c895f 100644 --- a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs @@ -3,8 +3,7 @@ #![allow(suspicious_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int}; extern "C" { fn open(path: *const c_char, oflag: c_int, ...) -> c_int; @@ -13,7 +12,7 @@ extern "C" { } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) } as u32; diff --git a/src/tools/miri/tests/fail/shims/return_type_mismatch.rs b/src/tools/miri/tests/fail/shims/return_type_mismatch.rs index bcde5d8040f59..1b22dfbd44872 100644 --- a/src/tools/miri/tests/fail/shims/return_type_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/return_type_mismatch.rs @@ -3,8 +3,7 @@ #![allow(suspicious_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int, c_short}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int, c_short}; extern "C" { fn open(path: *const c_char, oflag: c_int, ...) -> c_int; @@ -13,7 +12,7 @@ extern "C" { } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) }; diff --git a/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs b/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs index ce0c6c289faf0..c00a1f0b22814 100644 --- a/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs @@ -3,8 +3,7 @@ #![allow(invalid_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int}; // Declare a variadic function as non-variadic. extern "C" { @@ -12,7 +11,7 @@ extern "C" { } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let _fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) //~^ ERROR: is a c-variadic function, but the caller is using a non-variadic signature diff --git a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs index 34f6ae24f3e1d..f4e7a02b838fa 100644 --- a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs +++ b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs @@ -3,15 +3,14 @@ #![allow(invalid_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int}; extern "C" { fn open(path: *const c_char, ...) -> c_int; } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let _fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) //~^ ERROR: takes 2 fixed (non-variadic) arguments, but 1 argument was given 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 819717fed8523..dd79c96e179c8 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 @@ -3,9 +3,8 @@ //@compile-flags: -Zmiri-disable-isolation //@run-native -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::mem::MaybeUninit; -use std::os::unix::ffi::OsStrExt; #[path = "../../utils/mod.rs"] mod utils; @@ -28,7 +27,7 @@ fn getmod(path: &CStr) -> u32 { fn test_chmod() { let path = utils::prepare_with_content("miri_test_libc_chmod.txt", b"abcdef"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); unsafe { errno_check(libc::chmod(c_path.as_ptr(), 0o777)) }; assert_eq!(getmod(&c_path), 0o777); @@ -38,7 +37,7 @@ fn test_chmod() { fn test_fchmod() { let path = utils::prepare_with_content("miri_test_libc_chmod.txt", b"abcdef"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); let fd = unsafe { errno_result(libc::open(c_path.as_ptr(), libc::O_RDONLY)).unwrap() }; unsafe { errno_check(libc::fchmod(fd, 0o777)) }; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs deleted file mode 100644 index 4fe66c338a045..0000000000000 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Symlink tests are separate since they don't in general work on a Windows host. -//@ignore-host: windows # creating symlinks requires admin permissions on Windows -//@ignore-target: windows # File handling is not implemented yet -//@compile-flags: -Zmiri-disable-isolation -//@run-native - -use std::ffi::CString; -use std::io::ErrorKind; -use std::os::unix::ffi::OsStrExt; - -#[path = "../../utils/libc.rs"] -mod libc_utils; -#[path = "../../utils/mod.rs"] -mod utils; -use libc_utils::errno_result; - -fn main() { - test_readlink(); - test_nofollow_symlink(); -} - -fn test_readlink() { - let bytes = b"Hello, World!\n"; - let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); - let expected_path = path.as_os_str().as_bytes(); - - let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - - // Test that the expected string gets written to a buffer of proper - // length, and that a trailing null byte is not written. - let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - let symlink_c_ptr = symlink_c_str.as_ptr(); - - // Make the buf one byte larger than it needs to be, - // and check that the last byte is not overwritten. - let mut large_buf = vec![0xFF; expected_path.len() + 1]; - let res = errno_result(unsafe { - libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) - }) - .unwrap(); - // Check that the resolved path was properly written into the buf. - assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); - assert_eq!(large_buf.last(), Some(&0xFF)); - assert_eq!(res, (large_buf.len() - 1) as isize); - - // Test that the resolved path is truncated if the provided buffer - // is too small. - let mut small_buf = [0u8; 2]; - let res = errno_result(unsafe { - libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) - }) - .unwrap(); - assert_eq!(small_buf, &expected_path[..small_buf.len()]); - assert_eq!(res, small_buf.len() as isize); - - // Test that we report a proper error for a missing path. - let err = errno_result(unsafe { - libc::readlink( - c"MIRI_MISSING_FILE_NAME".as_ptr(), - small_buf.as_mut_ptr().cast(), - small_buf.len(), - ) - }) - .unwrap_err(); - assert_eq!(err.kind(), ErrorKind::NotFound); -} - -fn test_nofollow_symlink() { - let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", b"Hello, World!\n"); - - let symlink_path = utils::prepare("test_nofollow_symlink.txt"); - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - - let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - - let err = errno_result(unsafe { - libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) - }) - .unwrap_err(); - assert_eq!(err.raw_os_error(), Some(libc::ELOOP)); -} 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 d5418d3612a42..b3f063ef07cba 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -5,10 +5,11 @@ use std::ffi::{CStr, CString, OsString}; use std::fs::{self, File, canonicalize, create_dir, remove_dir, remove_file}; use std::io::{ErrorKind, Write}; +use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; use std::path::PathBuf; -use std::ptr; +use std::{env, ptr}; #[path = "../../utils/mod.rs"] mod utils; @@ -27,10 +28,10 @@ fn main() { test_ftruncate::(libc::ftruncate); #[cfg(target_os = "linux")] test_ftruncate::(libc::ftruncate64); - test_file_open_unix_allow_two_args(); - test_file_open_unix_needs_three_args(); - test_file_open_unix_extra_third_arg(); - test_file_open_dir(); + test_file_open_allow_two_args(); + test_file_open_needs_three_args(); + test_file_open_extra_third_arg(); + test_file_open_nofollow(); #[cfg(target_os = "linux")] test_o_tmpfile_flag(); test_posix_mkstemp(); @@ -49,24 +50,22 @@ fn main() { test_fallocate::(libc::fallocate64); #[cfg(target_os = "linux")] test_sync_file_range(); + test_opendir_closedir(); + test_readdir(); + test_dirfd(); test_fstat(); + test_fstatat(); test_stat(); test_lstat(); test_futimens(); test_isatty(); test_read_and_uninit(); - test_nofollow_not_symlink(); #[cfg(target_os = "macos")] test_ioctl(); - test_opendir_closedir(); - test_readdir(); - test_dirfd(); #[cfg(target_os = "linux")] - test_statx_on_file_path(); + test_statx(); #[cfg(target_os = "linux")] - test_statx_on_file_descriptor(); - #[cfg(target_os = "linux")] - test_statx_empty_path_on_pipe(); + test_statx_on_empty_path(); test_readv(); test_readv_empty_bufs(); #[cfg(not(target_os = "solaris"))] @@ -77,6 +76,7 @@ fn main() { #[cfg(not(target_os = "solaris"))] test_pwritev(); test_pwrite(); + test_readlink(); test_linkat(); } @@ -115,61 +115,55 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &fs::Metadata, expecte } #[cfg(target_os = "linux")] -fn test_statx_on_file_descriptor() { - use std::mem::MaybeUninit; - +fn test_statx() { let bytes = b"hello"; - let path = utils::prepare_with_content("miri_test_libc_statx_fd.txt", bytes); - let file = File::open(&path).unwrap(); - let meta = file.metadata().unwrap(); + let path = utils::prepare_with_content("miri_test_libc_statx.txt", bytes); + let c_path = utils::into_c_string(&path); + let meta = fs::metadata(&path).unwrap(); + // Relative to working directory. unsafe { + let old_cwd = env::current_dir().unwrap(); + env::set_current_dir(path.parent().unwrap()).unwrap(); + let mut stx = MaybeUninit::::zeroed(); errno_check(libc::statx( - file.as_raw_fd(), - c"".as_ptr(), - libc::AT_EMPTY_PATH, + libc::AT_FDCWD, + c"miri_test_libc_statx.txt".as_ptr(), + 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, stx.as_mut_ptr(), )); let stx = stx.assume_init(); assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + + env::set_current_dir(old_cwd).unwrap(); } - // If we don't set AT_EMPTY_PATH, we get an error. + // Setting AT_EMPTY_PATH is fine even if the path is not actually empty. unsafe { let mut stx = MaybeUninit::::zeroed(); - let err = errno_result(libc::statx( - file.as_raw_fd(), - c"".as_ptr(), - 0, + errno_check(libc::statx( + libc::AT_FDCWD, + c_path.as_ptr(), + libc::AT_EMPTY_PATH, libc::STATX_BASIC_STATS | libc::STATX_BTIME, stx.as_mut_ptr(), - )) - .unwrap_err(); - assert_eq!(err.raw_os_error().unwrap(), libc::ENOENT); - } - - drop(file); - remove_file(&path).unwrap(); -} - -#[cfg(target_os = "linux")] -fn test_statx_on_file_path() { - use std::mem::MaybeUninit; + )); - let bytes = b"hello"; - let path = utils::prepare_with_content("miri_test_libc_statx.txt", bytes); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); - let meta = fs::metadata(&path).unwrap(); + let stx = stx.assume_init(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + // dirfd is ignored because our path is absolute. + assert!(path.is_absolute()); unsafe { let mut stx = MaybeUninit::::zeroed(); errno_check(libc::statx( - libc::AT_FDCWD, + 999, // dirfd c_path.as_ptr(), - 0, + libc::AT_EMPTY_PATH, libc::STATX_BASIC_STATS | libc::STATX_BTIME, stx.as_mut_ptr(), )); @@ -178,28 +172,48 @@ fn test_statx_on_file_path() { assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); } - // Setting AT_EMPTY_PATH is fine even if the path is not actually empty. + // Symlink following. + let symlinkpath = utils::prepare("miri_test_libc_statx.link"); + let c_symlinkpath = utils::into_c_string(&symlinkpath); + std::os::unix::fs::symlink(&path, &symlinkpath).unwrap(); unsafe { let mut stx = MaybeUninit::::zeroed(); errno_check(libc::statx( - libc::AT_FDCWD, - c_path.as_ptr(), + 999, // dirfd + c_symlinkpath.as_ptr(), libc::AT_EMPTY_PATH, libc::STATX_BASIC_STATS | libc::STATX_BTIME, stx.as_mut_ptr(), )); - let stx = stx.assume_init(); assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); } - - // dirfd is ignored because our path is absolute. - assert!(path.is_absolute()); unsafe { let mut stx = MaybeUninit::::zeroed(); errno_check(libc::statx( 999, // dirfd - c_path.as_ptr(), + c_symlinkpath.as_ptr(), + libc::AT_SYMLINK_NOFOLLOW, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )); + let stx = stx.assume_init(); + assert!(stx.stx_mask & libc::STATX_TYPE != 0); + assert_eq!((stx.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFLNK); + assert!(stx.stx_mask & libc::STATX_MODE != 0); + assert_ne!((stx.stx_mode as libc::mode_t) & !libc::S_IFMT, 0); + } + + // Relative to a dirfd. + // The only way to get a dirfd in Miri currently is via a `dirfd`. Slightly silly, but whatever. + let dirstream = unsafe { libc::opendir(utils::into_c_string(path.parent().unwrap()).as_ptr()) }; + assert!(!dirstream.is_null()); + let dirfd = unsafe { libc::dirfd(dirstream) }; + unsafe { + let mut stx = MaybeUninit::::zeroed(); + errno_check(libc::statx( + dirfd, + c"miri_test_libc_statx.txt".as_ptr(), libc::AT_EMPTY_PATH, libc::STATX_BASIC_STATS | libc::STATX_BTIME, stx.as_mut_ptr(), @@ -208,17 +222,56 @@ fn test_statx_on_file_path() { let stx = stx.assume_init(); assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); } + errno_check(unsafe { libc::closedir(dirstream) }); remove_file(&path).unwrap(); + remove_file(&symlinkpath).unwrap(); } #[cfg(target_os = "linux")] -fn test_statx_empty_path_on_pipe() { +fn test_statx_on_empty_path() { + let bytes = b"hello"; + let path = utils::prepare_with_content("miri_test_libc_statx_fd.txt", bytes); + let file = File::open(&path).unwrap(); + let meta = file.metadata().unwrap(); + + unsafe { + let mut stx = MaybeUninit::::zeroed(); + errno_check(libc::statx( + file.as_raw_fd(), + c"".as_ptr(), + libc::AT_EMPTY_PATH, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )); + + let stx = stx.assume_init(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + + // If we don't set AT_EMPTY_PATH, we get an error. + unsafe { + let mut stx = MaybeUninit::::zeroed(); + let err = errno_result(libc::statx( + file.as_raw_fd(), + c"".as_ptr(), + 0, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )) + .unwrap_err(); + assert_eq!(err.raw_os_error().unwrap(), libc::ENOENT); + } + + drop(file); + remove_file(&path).unwrap(); + + // Test it on a pipe as well. unsafe { let mut fds = [0; 2]; errno_check(libc::pipe(fds.as_mut_ptr())); - let mut statx_buf = std::mem::MaybeUninit::::zeroed(); + let mut statx_buf = MaybeUninit::::zeroed(); errno_check(libc::statx( fds[0], @@ -251,50 +304,50 @@ fn test_statx_empty_path_on_pipe() { } } -fn test_file_open_unix_allow_two_args() { - let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); +fn test_file_open_allow_two_args() { + let path = utils::prepare_with_content("miri_test_file_open_allow_two_args.txt", &[]); + let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY) }).unwrap(); } -fn test_file_open_unix_needs_three_args() { - let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); +fn test_file_open_needs_three_args() { + let path = utils::prepare_with_content("miri_test_file_open_needs_three_args.txt", &[]); + let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_CREAT | libc::O_RDWR, 0o666) }) .unwrap(); } -fn test_file_open_unix_extra_third_arg() { - let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); +fn test_file_open_extra_third_arg() { + let path = utils::prepare_with_content("miri_test_file_open_extra_third_arg.txt", &[]); + let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY, 42) }).unwrap(); } -fn test_file_open_dir() { - let dir_path = utils::prepare_dir("miri_test_fs_dir"); - create_dir(&dir_path).unwrap(); - let dir_name = CString::new(dir_path.into_os_string().into_encoded_bytes()).unwrap(); +fn test_file_open_nofollow() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_nofollow_not_symlink.txt", bytes); + let cpath = utils::into_c_string(path); + let fd = + errno_result(unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }) + .unwrap(); + errno_check(unsafe { libc::close(fd) }); - // Opening it for read-write fails. The error code differs between Unix and Windows hosts. - let err = errno_result(unsafe { libc::open(dir_name.as_ptr(), libc::O_RDWR) }).unwrap_err(); - assert!( - [libc::EISDIR, libc::EPERM].contains(&err.raw_os_error().unwrap()), - "unexpected errno: {err}" - ); + let path = utils::prepare_with_content("miri_test_open_nofollow_symlink_target.txt", bytes); + + let symlink_path = utils::prepare("miri_test_open_nofollow_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - // Opening it for reading succeeds, but then reading fails. - // FIXME: currently does not behave as expected on Windows hosts. - // See . - // let fd = errno_result(unsafe { libc::open(dir_name.as_ptr(), libc::O_RDONLY) }).unwrap(); - // let mut buf = [0u8; 4]; - // let err = - // errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) }).unwrap_err(); - // assert_eq!(err.raw_os_error().unwrap(), libc::EISDIR, "unexpected errno: {err}"); - // errno_check(unsafe { libc::close(fd) }); + let symlink_cpath = utils::into_c_string(symlink_path); + + let err = errno_result(unsafe { + libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) + }) + .unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::ELOOP)); } fn test_dup_stdout_stderr() { @@ -311,7 +364,7 @@ fn test_dup_stdout_stderr() { fn test_dup() { let bytes = b"dup and dup2"; let path = utils::prepare_with_content("miri_test_libc_dup.txt", bytes); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); unsafe { let fd = errno_result(libc::open(name.as_ptr(), libc::O_RDONLY)).unwrap(); @@ -373,8 +426,8 @@ fn test_rename() { let file = File::create(&path1).unwrap(); drop(file); - let c_path1 = CString::new(path1.as_os_str().as_bytes()).expect("CString::new failed"); - let c_path2 = CString::new(path2.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path1 = utils::into_c_string(&path1); + let c_path2 = utils::into_c_string(&path2); // Renaming should succeed unsafe { libc::rename(c_path1.as_ptr(), c_path2.as_ptr()) }; @@ -404,7 +457,7 @@ fn test_ftruncate>( file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }; // Truncate to a bigger size @@ -506,11 +559,11 @@ fn test_posix_mkstemp() { /// Test allocating variant of `realpath`. fn test_posix_realpath_alloc() { - use std::os::unix::ffi::{OsStrExt, OsStringExt}; + use std::os::unix::ffi::OsStringExt; let buf; let path = utils::prepare("miri_test_libc_posix_realpath_alloc"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); // Create file. drop(File::create(&path).unwrap()); @@ -529,11 +582,10 @@ fn test_posix_realpath_alloc() { /// Test non-allocating variant of `realpath`. fn test_posix_realpath_noalloc() { - use std::ffi::{CStr, CString}; - use std::os::unix::ffi::OsStrExt; + use std::ffi::CStr; let path = utils::prepare("miri_test_libc_posix_realpath_noalloc"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let mut v = vec![0; libc::PATH_MAX as usize]; @@ -554,16 +606,12 @@ fn test_posix_realpath_noalloc() { /// Test failure cases for `realpath`. fn test_posix_realpath_errors() { - use std::ffi::CString; - use std::io::ErrorKind; - // Test nonexistent path returns an error. - let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed"); + let c_path = c"./nothing_to_see_here"; let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) }; assert!(r.is_null()); let e = std::io::Error::last_os_error(); assert_eq!(e.raw_os_error(), Some(libc::ENOENT)); - assert_eq!(e.kind(), ErrorKind::NotFound); } #[cfg(target_os = "linux")] @@ -615,7 +663,7 @@ fn test_posix_fallocate>( assert_eq!(ret, libc::EINVAL); // fd not writable - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDONLY) }; let ret = unsafe { posix_fallocate(fd, T::from(0), T::from(10)) }; assert_eq!(ret, libc::EBADF); @@ -629,7 +677,7 @@ fn test_posix_fallocate>( file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }; // Allocate to a bigger size from offset 0 @@ -692,7 +740,7 @@ fn test_fallocate>( assert_eq!(err.raw_os_error().unwrap(), libc::EINVAL); // fd not writable - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); let fd = errno_result(unsafe { libc::open(c_path.as_ptr(), libc::O_RDONLY) }).unwrap(); let err = errno_result(unsafe { fallocate(fd, 0, T::from(0), T::from(10)) }).unwrap_err(); assert_eq!(err.raw_os_error().unwrap(), libc::EBADF); @@ -705,7 +753,7 @@ fn test_fallocate>( file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let fd = errno_result(unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }).unwrap(); // Allocate to a bigger size from offset 0 @@ -770,7 +818,6 @@ fn test_sync_file_range() { } fn test_fstat() { - use std::mem::MaybeUninit; use std::os::unix::io::AsRawFd; let path = utils::prepare_with_content("miri_test_libc_fstat.txt", b"hello"); @@ -789,17 +836,97 @@ fn test_fstat() { check_stat_fields(stat); remove_file(&path).unwrap(); + + // Test it on a pipe as well. + unsafe { + let mut fds = [0; 2]; + errno_check(libc::pipe(fds.as_mut_ptr())); + + let mut stat = MaybeUninit::::uninit(); + errno_check(libc::fstat(fds[0], stat.as_mut_ptr())); + let stat = stat.assume_init_ref(); + + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFIFO); + assert_ne!(stat.st_mode & !libc::S_IFMT, 0, "some permission should be set"); + assert_eq!(stat.st_size, 0); + + errno_check(libc::close(fds[0])); + errno_check(libc::close(fds[1])); + } +} + +fn test_fstatat() { + let testdir = utils::prepare_dir("miri_test_fstatat"); + fs::create_dir(&testdir).unwrap(); + let filename = "file.txt"; + let cfilename = c"file.txt"; + fs::write(testdir.join(filename), b"hello").unwrap(); + let absfilename = testdir.join(filename); + assert!(absfilename.is_absolute()); + + // The only way to get a dirfd in Miri currently is via a `dirfd`. Slightly silly, but whatever. + let dirstream = unsafe { libc::opendir(utils::into_c_string(&testdir).as_ptr()) }; + assert!(!dirstream.is_null()); + let dirfd = unsafe { libc::dirfd(dirstream) }; + + let checkstat = |stat: &libc::stat| { + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG); + assert_ne!(stat.st_mode & !libc::S_IFMT, 0, "some permission should be set"); + assert_eq!(stat.st_size, 5); + check_stat_fields(stat); + }; + + // Relative to working directory. + let old_cwd = env::current_dir().unwrap(); + env::set_current_dir(&testdir).unwrap(); + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { libc::fstatat(libc::AT_FDCWD, cfilename.as_ptr(), stat.as_mut_ptr(), 0) }); + checkstat(unsafe { stat.assume_init_ref() }); + env::set_current_dir(old_cwd).unwrap(); + + // Absolute path, dirfd is ignored. + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { + libc::fstatat( + 999, // dirfd + utils::into_c_string(&absfilename).as_ptr(), + stat.as_mut_ptr(), + 0, // flags + ) + }); + checkstat(unsafe { stat.assume_init_ref() }); + + // Relative to dirfd. + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { libc::fstatat(dirfd, cfilename.as_ptr(), stat.as_mut_ptr(), 0) }); + checkstat(unsafe { stat.assume_init_ref() }); + + // Symlink following. + let linkname = testdir.join("link"); + std::os::unix::fs::symlink(&absfilename, &linkname).unwrap(); + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { libc::fstatat(dirfd, c"link".as_ptr(), stat.as_mut_ptr(), 0) }); + checkstat(unsafe { stat.assume_init_ref() }); + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { + libc::fstatat(dirfd, c"link".as_ptr(), stat.as_mut_ptr(), libc::AT_SYMLINK_NOFOLLOW) + }); + let stat = unsafe { stat.assume_init_ref() }; + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFLNK); // not S_IFREG! + assert_ne!(stat.st_mode & !libc::S_IFMT, 0, "some permission should be set"); + check_stat_fields(stat); + + errno_check(unsafe { libc::closedir(dirstream) }); } fn test_stat() { - use std::mem::MaybeUninit; // Also make sure we *do* follow symlinks. let path = utils::prepare_with_content("miri_test_libc_stat.txt", b"hello"); let symlink_path = utils::prepare("miri_test_libc_lstat_symlink.txt"); std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - let cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let cpath = utils::into_c_string(symlink_path); let mut stat = MaybeUninit::::uninit(); errno_check(unsafe { libc::stat(cpath.as_ptr(), stat.as_mut_ptr()) }); @@ -816,13 +943,11 @@ fn test_stat() { } fn test_lstat() { - use std::mem::MaybeUninit; - let path = utils::prepare_with_content("miri_test_libc_lstat.txt", b"hello"); let symlink_path = utils::prepare("miri_test_libc_lstat_symlink.txt"); std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - let cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let cpath = utils::into_c_string(&symlink_path); let mut stat = MaybeUninit::::uninit(); errno_check(unsafe { libc::lstat(cpath.as_ptr(), stat.as_mut_ptr()) }); @@ -839,7 +964,6 @@ fn test_lstat() { } fn test_futimens() { - use std::mem::MaybeUninit; use std::time::{SystemTime, UNIX_EPOCH}; let path = utils::prepare_with_content("miri_test_libc_futimens.txt", b"hello"); @@ -905,7 +1029,7 @@ fn test_isatty() { libc::isatty(libc::STDERR_FILENO); // But when we open a file, it is definitely not a TTY. - let path = utils::prepare("notatty.txt"); + let path = utils::prepare("miri_notatty.txt"); let file = File::create(&path).unwrap(); assert_eq!(libc::isatty(file.as_raw_fd()), 0); @@ -918,15 +1042,14 @@ fn test_isatty() { } fn test_read_and_uninit() { - use std::mem::MaybeUninit; { // We test that libc::read initializes its buffer. - let path = utils::prepare_with_content("pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let path = utils::prepare_with_content("miri-pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); + let cpath = utils::into_c_string(path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); - let mut buf: MaybeUninit = std::mem::MaybeUninit::uninit(); + let mut buf: MaybeUninit = MaybeUninit::uninit(); assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 1), 1); let buf = buf.assume_init(); assert_eq!(buf, 1); @@ -938,8 +1061,8 @@ fn test_read_and_uninit() { // We test that if we requested to read 4 bytes, but actually read 3 bytes, then // 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is. let data = [1u8, 2, 3]; - let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &data); - let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); + let path = utils::prepare_with_content("miri-pass-libc-read-and-uninit-2.txt", &data); + let cpath = utils::into_c_string(&path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); @@ -959,18 +1082,10 @@ fn test_read_and_uninit() { } } -fn test_nofollow_not_symlink() { - let bytes = b"Hello, World!\n"; - let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes); - let cpath = CString::new(path.as_os_str().as_bytes()).unwrap(); - let ret = unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }; - assert!(ret >= 0); -} - #[cfg(target_os = "macos")] fn test_ioctl() { let path = utils::prepare_with_content("miri_test_libc_ioctl.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); unsafe { // 100 surely is an invalid FD. @@ -987,7 +1102,7 @@ fn test_opendir_closedir() { // dir should exist 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"); + let cpath = utils::into_c_string(&path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); errno_check(unsafe { libc::closedir(dir) }); @@ -1001,8 +1116,8 @@ fn test_opendir_closedir() { assert_eq!(e.kind(), ErrorKind::NotFound); // open normal file as dir should fail - let file_path = utils::prepare_with_content("test_not_a_dir.txt", b"hello"); - let cfile = CString::new(file_path.as_os_str().as_bytes()).expect("CString::new failed"); + let file_path = utils::prepare_with_content("miri_test_not_a_dir.txt", b"hello"); + let cfile = utils::into_c_string(&file_path); let dir: *mut libc::DIR = unsafe { libc::opendir(cfile.as_ptr()) }; assert!(dir.is_null()); let e = std::io::Error::last_os_error(); @@ -1023,7 +1138,7 @@ fn test_readdir() { write(&file1, b"content1").unwrap(); write(&file2, b"content2").unwrap(); - let c_path = CString::new(dir_path.as_os_str().as_bytes()).unwrap(); + let c_path = utils::into_c_string(&dir_path); unsafe { let dirp = libc::opendir(c_path.as_ptr()); @@ -1050,11 +1165,9 @@ fn test_readdir() { } fn test_dirfd() { - use std::mem::MaybeUninit; - 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"); + let cpath = utils::into_c_string(path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); @@ -1100,8 +1213,8 @@ pub fn check_stat_fields(stat: &libc::stat) { /// 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 path = utils::prepare_with_content("miri-pass-libc-readv.txt", &file_contents); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1136,8 +1249,8 @@ fn test_readv_empty_bufs() { return; } - 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 path = utils::prepare_with_content("miri-pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); + let cpath = utils::into_c_string(path); 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) }; @@ -1150,8 +1263,8 @@ fn test_readv_empty_bufs() { #[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 path = utils::prepare_with_content("miri-pass-libc-preadv.txt", &file_contents); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1191,8 +1304,8 @@ fn test_preadv() { /// 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 path = utils::prepare_with_content("miri-pass-libc-pread.txt", &file_contents); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1218,8 +1331,8 @@ fn test_pread() { /// 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 path = utils::prepare_with_content("miri-pass-libc-writev.txt", &[]); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1269,8 +1382,8 @@ fn test_writev_empty_bufs() { return; } - 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 path = utils::prepare_with_content("miri-pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); unsafe { @@ -1284,8 +1397,8 @@ fn test_writev_empty_bufs() { /// 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 path = utils::prepare_with_content("miri-pass-libc-pwritev.txt", &[]); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1340,8 +1453,8 @@ fn test_pwritev() { /// 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 path = utils::prepare_with_content("miri-pass-libc-pwritev.txt", &[]); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1381,12 +1494,59 @@ fn test_pwrite() { assert_eq!(&write_buffer[0..bytes_written], &read_buffer[OFFSET..(bytes_written + OFFSET)]); } +fn test_readlink() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); + let expected_path = path.as_os_str().as_bytes(); + + let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + + // Test that the expected string gets written to a buffer of proper + // length, and that a trailing null byte is not written. + let symlink_c_str = utils::into_c_string(symlink_path); + let symlink_c_ptr = symlink_c_str.as_ptr(); + + // Make the buf one byte larger than it needs to be, + // and check that the last byte is not overwritten. + let mut large_buf = vec![0xFF; expected_path.len() + 1]; + let res = errno_result(unsafe { + libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) + }) + .unwrap(); + // Check that the resolved path was properly written into the buf. + assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); + assert_eq!(large_buf.last(), Some(&0xFF)); + assert_eq!(res, (large_buf.len() - 1) as isize); + + // Test that the resolved path is truncated if the provided buffer + // is too small. + let mut small_buf = [0u8; 2]; + let res = errno_result(unsafe { + libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) + }) + .unwrap(); + assert_eq!(small_buf, &expected_path[..small_buf.len()]); + assert_eq!(res, small_buf.len() as isize); + + // Test that we report a proper error for a missing path. + let err = errno_result(unsafe { + libc::readlink( + c"MIRI_MISSING_FILE_NAME".as_ptr(), + small_buf.as_mut_ptr().cast(), + small_buf.len(), + ) + }) + .unwrap_err(); + assert_eq!(err.kind(), ErrorKind::NotFound); +} + fn test_linkat() { let source = utils::prepare_with_content("miri_test_libc_linkat_source.txt", b"hello"); let link = utils::prepare("miri_test_libc_linkat_link.txt"); - let c_source = CString::new(source.as_os_str().as_bytes()).expect("CString::new failed"); - let c_link = CString::new(link.as_os_str().as_bytes()).expect("CString::new failed"); + let c_source = utils::into_c_string(&source); + let c_link = utils::into_c_string(&link); // Call linkat unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs index 4e46870da204b..40a3a60068d90 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs @@ -7,7 +7,6 @@ mod libc_utils; #[path = "../../utils/mod.rs"] mod utils; -use std::ffi::CString; use std::ptr; use libc_utils::*; @@ -15,8 +14,8 @@ use libc_utils::*; /// Test doing address resolution using the `getaddrinfo` syscall. /// This also tests freeing the address linked list using `freeaddrinfo`. fn main() { - let node_c_str = CString::new("this-is-not-a-valid-address").unwrap(); - let service_c_str = CString::new("8080").unwrap(); + let node_c_str = c"this-is-not-a-valid-address"; + let service_c_str = c"8080"; let mut hints: libc::addrinfo = unsafe { std::mem::zeroed() }; hints.ai_socktype = libc::SOCK_STREAM; 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 c91f3474793e3..563c2a3842a19 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 @@ -57,9 +57,9 @@ fn test_connect_nonblock() { net::accept_ipv4(server_sockfd).unwrap(); }); - // Non-blocking connects always "fail" with EINPROGRESS. - let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InProgress); + let result = net::connect_ipv4(client_sockfd, addr); + // This might succeed immediately or return EINPROGRESS. + assert!(result.is_ok() || result.is_err_and(|e| e.kind() == ErrorKind::InProgress)); // Add client socket with WRITABLE interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLET | EPOLLERR).unwrap(); @@ -145,9 +145,10 @@ fn test_connect_nonblock_err() { // 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); + // Since the address is not bound, the `connect` can never succeed + // immediately and thus it always "fails" with EINPROGRESS. + assert!(err.kind() == ErrorKind::InProgress); // Add interest for client socket. epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLET | libc::EPOLLERR).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs index de718feb47990..0325fe06dab82 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs @@ -58,6 +58,9 @@ fn main() { test_getpeername_ipv4_nonblock(); test_getpeername_ipv4_nonblock_no_peer(); + + test_listen_connecting(); + test_connect_connecting(); } /// Test that setting the O_NONBLOCK flag changes the blocking state of a socket. @@ -225,15 +228,22 @@ fn test_connect_nonblock() { // Yield to server thread to ensure that it's currently accepting. thread::sleep(Duration::from_millis(10)); - // Non-blocking connects always "fail" with EINPROGRESS. - let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + let Err(err) = net::connect_ipv4(client_sockfd, addr) else { + // The connection could be established immediately. We don't + // need to enter the loop and check whether the connection could + // really be established. + return server_thread.join().unwrap(); + }; + // The connection could not be established immediately. The `connect` + // should thus return EINPROGRESS. assert_eq!(err.kind(), ErrorKind::InProgress); loop { // There should be no error during async connection. - let errno = net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR) - .unwrap(); - assert_eq!(errno, 0); + let so_error = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR) + .unwrap(); + assert_eq!(so_error, 0); let result = net::sockname_ipv4(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) @@ -667,9 +677,10 @@ fn test_getsockname_ipv4_connect_nonblock() { // a zero port. let addr = net::sock_addr_ipv4([192, 0, 2, 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); + // Since we're connecting to a blackhole address, the `connect` can + // never succeed immediately and thus it always "fails" with EINPROGRESS. + assert!(err.kind() == ErrorKind::InProgress); let (_, sock_addr) = net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(client_sockfd, storage, len) @@ -712,8 +723,14 @@ fn test_getpeername_ipv4_nonblock() { // Yield to server thread to ensure that it's currently accepting. thread::sleep(Duration::from_millis(10)); - // Non-blocking connects always "fail" with EINPROGRESS. - let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + let Err(err) = net::connect_ipv4(client_sockfd, addr) else { + // The connection could be established immediately. We don't + // need to enter the loop and check whether the connection could + // really be established. + return server_thread.join().unwrap(); + }; + // The connection could not be established immediately. The `connect` + // should thus return EINPROGRESS. assert_eq!(err.kind(), ErrorKind::InProgress); loop { @@ -764,14 +781,15 @@ fn test_getpeername_ipv4_nonblock_no_peer() { // a zero port. let addr = net::sock_addr_ipv4([192, 0, 2, 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); + // Since we're connecting to a blackhole address, the `connect` can + // never succeed immediately and thus it always "fails" with EINPROGRESS. + assert!(err.kind() == ErrorKind::InProgress); // There should be no error during async connection. - let errno = + let so_error = net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); - assert_eq!(errno, 0); + assert_eq!(so_error, 0); // Since we're never accepting the connection, the socket should never be // successfully connected and thus we should be unable to read the peername. @@ -782,3 +800,81 @@ fn test_getpeername_ipv4_nonblock_no_peer() { }; assert_eq!(err.kind(), ErrorKind::NotConnected); } + +/// Test that `listen` returns EINVAL for a non-blocking IPv4 +/// socket which is stuck at connecting to a remote address. +fn test_listen_connecting() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 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 `192.0.2.1` we know that nothing is + // listening because it's a blackhole address: + // + // 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([192, 0, 2, 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); + + // There should be no error during async connection. + let so_error = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); + assert_eq!(so_error, 0); + + // Connecting sockets cannot start listening, thus the operation should + // fail with EINVAL. + let err = unsafe { errno_result(libc::listen(client_sockfd, 16)).unwrap_err() }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that invoking `connect` on an already connecting client +/// TCP socket returns EALREADY. +fn test_connect_connecting() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 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 `192.0.2.1` we know that nothing is + // listening because it's a blackhole address: + // + // 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([192, 0, 2, 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); + + // There should be no error during async connection. + let so_error = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); + assert_eq!(so_error, 0); + + // Create a socket which listens on `addr`. + let (_, addr) = net::make_listener_ipv4().unwrap(); + + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + // The standard library doesn't provide an error kind for EALREADY; + // we thus cannot assert the correct error kind. + assert_eq!(err.raw_os_error(), Some(libc::EALREADY)); +} 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 85d6302a0175d..d581f358cac95 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -33,10 +33,17 @@ fn main() { } test_bind_ipv4_invalid_addr_len(); test_bind_ipv6(); + test_bind_twice(); + test_bind_connected(); + test_bind_listening(); test_listen(); + test_listen_connected(); + test_listen_listening(); test_accept_connect(); test_connect_error(); + test_connect_connected(); + test_connect_listening(); test_send_peek_recv(); test_write_read(); test_readv(); @@ -47,6 +54,7 @@ fn main() { test_getsockname_ipv4_unbound(); test_getsockname_ipv4_connect(); test_getsockname_ipv6(); + test_getsockname_ipv6_unbound(); test_getpeername_ipv4(); test_getpeername_ipv6(); @@ -236,21 +244,123 @@ fn test_bind_ipv6() { } } -fn test_listen() { +/// Test that invoking `bind` on an already bound TCP socket +/// returns EINVAL. +fn test_bind_twice() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); - unsafe { + let err = unsafe { errno_check(libc::bind( sockfd, (&addr as *const libc::sockaddr_in).cast::(), size_of::() as libc::socklen_t, )); - } + errno_result(libc::bind( + sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )) + .unwrap_err() + }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that invoking `bind` on an already connected client +/// TCP socket returns EINVAL. +fn test_bind_connected() { + 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(); + net::accept_ipv4(server_sockfd).unwrap(); + + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + let err = unsafe { + errno_result(libc::bind( + client_sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )) + .unwrap_err() + }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that invoking `bind` on an already listening server +/// TCP socket returns EINVAL. +fn test_bind_listening() { + let server_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; unsafe { - errno_check(libc::listen(sockfd, 16)); + errno_check(libc::listen(server_sockfd, 16)); } + + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + let err = unsafe { + errno_result(libc::bind( + server_sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )) + .unwrap_err() + }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that a socket can start listening. This also tests +/// whether sockets are bound implicitly after `listen` has been +/// invoked on them. +fn test_listen() { + let sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + unsafe { errno_check(libc::listen(sockfd, 16)) }; + + let (_, sock_addr) = + net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); + + // Libc representation of an unspecified IPv4 address with zero port. + let addr = net::sock_addr_ipv4([0, 0, 0, 0], 0); + + // Ensure that the socket got implicitly bound to the unspecified + // local address with a non-zero port. + assert!(sock_addr.sin_port > 0); + assert_eq!(addr.sin_family, sock_addr.sin_family); + assert_eq!(addr.sin_addr.s_addr, sock_addr.sin_addr.s_addr); +} + +/// Test that `listen` returns EINVAL for a client TCP socket which +/// is already connected to a peer socket. +fn test_listen_connected() { + 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(); + net::accept_ipv4(server_sockfd).unwrap(); + + // Connected sockets cannot start listening, thus the operation should + // fail with EINVAL. + let err = unsafe { errno_result(libc::listen(client_sockfd, 16)).unwrap_err() }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that `listen` succeeds for a server TCP socket which is already +/// listening. +fn test_listen_listening() { + let (server_sockfd, _) = net::make_listener_ipv4().unwrap(); + + // Invoking `listen` multiple times should be allowed as it can be used to change + // the backlog value. + unsafe { errno_check(libc::listen(server_sockfd, 16)) }; } /// Test accepting connections by running a server in a separate thread and connecting clients @@ -318,6 +428,46 @@ fn test_connect_error() { )); } +/// Test that invoking `connect` on an already connected client +/// TCP socket returns EISCONN. +fn test_connect_connected() { + let (server_sockfd1, addr1) = net::make_listener_ipv4().unwrap(); + let (_, addr2) = 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, addr1).unwrap(); + net::accept_ipv4(server_sockfd1).unwrap(); + + let err = net::connect_ipv4(client_sockfd, addr2).unwrap_err(); + // The standard library doesn't provide an error kind for EISCONN; + // we thus cannot assert the correct error kind. + assert_eq!(err.raw_os_error(), Some(libc::EISCONN)); +} + +/// Test that invoking `connect` on an already listening server +/// TCP socket returns EOPNOTSUPP on non-Linux-like targets and +/// EISCONN on Linux-like targets. +fn test_connect_listening() { + let (_, addr1) = net::make_listener_ipv4().unwrap(); + let (server_sockfd2, _) = net::make_listener_ipv4().unwrap(); + + let err = net::connect_ipv4(server_sockfd2, addr1).unwrap_err(); + + if cfg!(any(target_os = "linux", target_os = "android")) { + // Linux-like targets return EISCONN when attempting to invoke + // `connect` on an already listening socket. + + // The standard library doesn't provide an error kind for EISCONN; + // we thus cannot assert the correct error kind. + assert_eq!(err.raw_os_error(), Some(libc::EISCONN)); + } else { + // POSIX specifies EOPNOTSUPP when attempting to invoke + // `connect` on an already listening socket. + assert_eq!(err.raw_os_error(), Some(libc::EOPNOTSUPP)); + } +} + /// 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 @@ -606,6 +756,32 @@ fn test_getsockname_ipv6() { assert_eq!(addr.sin6_addr.s6_addr, sock_addr.sin6_addr.s6_addr); } +/// Test the `getsockname` syscall on an IPv6 socket which is not bound. +/// The `getsockname` syscall should return [::]:0 with flowinfo and scope id +/// of zero. +fn test_getsockname_ipv6_unbound() { + let sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; + + let (_, sock_addr) = + net::sockname_ipv6(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); + + // Libc representation of an unspecified IPv6 address with zero port, flowinfo and scope id. + let addr = net::sock_addr_full_ipv6( + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + /* port */ 0, + /* flowinfo */ 0, + /* scope_id */ 0, + ); + + assert_eq!(addr.sin6_family, sock_addr.sin6_family); + assert_eq!(addr.sin6_port, sock_addr.sin6_port); + assert_eq!(addr.sin6_flowinfo, sock_addr.sin6_flowinfo); + assert_eq!(addr.sin6_scope_id, sock_addr.sin6_scope_id); + assert_eq!(addr.sin6_addr.s6_addr, sock_addr.sin6_addr.s6_addr); +} + /// Test the `getpeername` syscall on an IPv4 socket. /// For a connected socket, the `getpeername` syscall should /// return the same address as the socket was connected to. diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index 80f8ef9359d3b..b012b7328eaf3 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -1,5 +1,7 @@ //@only-target: windows # this directly tests windows-only functions //@compile-flags: -Zmiri-disable-isolation +//@run-native + #![allow(nonstandard_style)] use std::io::{ErrorKind, Read, Seek, SeekFrom, Write}; @@ -19,15 +21,19 @@ use windows_sys::Win32::Foundation::{ }; use windows_sys::Win32::Storage::FileSystem::{ BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW, - FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, - FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, - FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FileAllocationInfo, FileEndOfFileInfo, - FlushFileBuffers, GetFileInformationByHandle, MoveFileExW, OPEN_ALWAYS, OPEN_EXISTING, - SetFileInformationByHandle, SetFilePointerEx, + FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, + FILE_BEGIN, FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, + FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, + FileAllocationInfo, FileEndOfFileInfo, FlushFileBuffers, GetFileInformationByHandle, + MoveFileExW, OPEN_ALWAYS, OPEN_EXISTING, SetFileInformationByHandle, SetFilePointerEx, }; use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; use windows_sys::Win32::System::Threading::GetCurrentProcess; +// Windows seems to usually but not always set FILE_ATTRIBUTE_ARCHIVE for new files. +// So we accept either "ARCHIVE" or "NORMAL". +const REGULAR_FILE: u32 = FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL; + fn main() { unsafe { test_create_dir_file(); @@ -71,7 +77,7 @@ unsafe fn test_create_dir_file() { } unsafe fn test_create_normal_file() { - let temp = utils::prepare("test_create_normal_file.txt"); + let temp = utils::prepare("miri_test_create_normal_file.txt"); let raw_path = to_wide_cstr(&temp); let handle = CreateFileW( raw_path.as_ptr(), @@ -87,9 +93,8 @@ unsafe fn test_create_normal_file() { if GetFileInformationByHandle(handle, &mut info) == 0 { panic!("Failed to get file information: {}", GetLastError()) }; - // FIXME: this test is wrong, somehow. It doesn't pass when run natively. - // - assert!(info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + assert!(info.dwFileAttributes & REGULAR_FILE != 0); + assert!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == 0); if CloseHandle(handle) == 0 { panic!("Failed to close file") }; @@ -121,7 +126,7 @@ unsafe fn test_create_normal_file() { if GetFileInformationByHandle(handle, &mut info) == 0 { panic!("Failed to get file information: {}", GetLastError()) }; - assert!(info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + assert!(info.dwFileAttributes & REGULAR_FILE != 0); if CloseHandle(handle) == 0 { panic!("Failed to close file") }; @@ -129,7 +134,7 @@ unsafe fn test_create_normal_file() { /// Tests that CREATE_ALWAYS sets the error value correctly based on whether the file already exists unsafe fn test_create_always_twice() { - let temp = utils::prepare("test_create_always.txt"); + let temp = utils::prepare("miri_test_create_always.txt"); let raw_path = to_wide_cstr(&temp); let handle = CreateFileW( raw_path.as_ptr(), @@ -164,7 +169,7 @@ unsafe fn test_create_always_twice() { /// Tests that OPEN_ALWAYS sets the error value correctly based on whether the file already exists unsafe fn test_open_always_twice() { - let temp = utils::prepare("test_open_always.txt"); + let temp = utils::prepare("miri_test_open_always.txt"); let raw_path = to_wide_cstr(&temp); let handle = CreateFileW( raw_path.as_ptr(), @@ -223,7 +228,7 @@ unsafe fn test_open_always_twice() { // } unsafe fn test_delete_file() { - let temp = utils::prepare("test_delete_file.txt"); + let temp = utils::prepare("miri_test_delete_file.txt"); let raw_path = to_wide_cstr(&temp); let _ = fs::File::create(&temp).unwrap(); @@ -244,7 +249,7 @@ unsafe fn test_ntstatus_to_dos() { } unsafe fn test_file_read_write() { - let temp = utils::prepare("test_file_read_write.txt"); + let temp = utils::prepare("miri_test_file_read_write.txt"); let file = fs::File::create(&temp).unwrap(); let handle = file.as_raw_handle(); @@ -295,7 +300,7 @@ unsafe fn test_file_read_write() { } unsafe fn test_set_file_info() { - let temp = utils::prepare("test_set_file.txt"); + let temp = utils::prepare("miri_test_set_file.txt"); let mut file = fs::File::create(&temp).unwrap(); let handle = file.as_raw_handle(); @@ -321,7 +326,7 @@ unsafe fn test_set_file_info() { } unsafe fn test_dup_handle() { - let temp = utils::prepare("test_dup.txt"); + let temp = utils::prepare("miri_test_dup.txt"); let mut file1 = fs::File::options().read(true).write(true).create(true).open(&temp).unwrap(); @@ -354,7 +359,7 @@ unsafe fn test_dup_handle() { } unsafe fn test_file_seek() { - let temp = utils::prepare("test_file_seek.txt"); + let temp = utils::prepare("miri_test_file_seek.txt"); let mut file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); file.write_all(b"Hello, World!\n").unwrap(); @@ -379,7 +384,7 @@ unsafe fn test_file_seek() { } unsafe fn test_flush_buffers() { - let temp = utils::prepare("test_flush_buffers.txt"); + let temp = utils::prepare("miri_test_flush_buffers.txt"); let file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); if FlushFileBuffers(file.as_raw_handle()) == 0 { panic!("Failed to flush buffers"); @@ -392,8 +397,8 @@ unsafe fn test_flush_buffers() { } unsafe fn test_move_file() { - let temp = utils::prepare("test_move_file.txt"); - let temp_new = utils::prepare("test_move_file_new.txt"); + let temp = utils::prepare("miri_test_move_file.txt"); + let temp_new = utils::prepare("miri_test_move_file_new.txt"); let mut file = fs::File::options().create(true).write(true).open(&temp).unwrap(); file.write_all(b"Hello, World!\n").unwrap(); diff --git a/src/tools/miri/tests/pass-dep/tokio/file-io.rs b/src/tools/miri/tests/pass-dep/tokio/file-io.rs index 982768111a511..d7704af1402e9 100644 --- a/src/tools/miri/tests/pass-dep/tokio/file-io.rs +++ b/src/tools/miri/tests/pass-dep/tokio/file-io.rs @@ -17,7 +17,7 @@ async fn main() { } async fn test_create_and_write() -> io::Result<()> { - let path = utils::prepare("foo.txt"); + let path = utils::prepare("miri_tokio_test_create_and_write.txt"); let mut file = File::create(&path).await?; // Write 10 bytes to the file. @@ -34,7 +34,7 @@ async fn test_create_and_write() -> io::Result<()> { async fn test_create_and_read() -> io::Result<()> { let bytes = b"more bytes"; - let path = utils::prepare_with_content("foo.txt", bytes); + let path = utils::prepare_with_content("miri_tokio_test_create_and_read.txt", bytes); let mut file = OpenOptions::new().read(true).open(&path).await.unwrap(); let mut buffer = vec![]; diff --git a/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs b/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs index 0d96bc2f5e737..fa83de8bc127e 100644 --- a/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs +++ b/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-num-cpus=1024 +// FIXME: Disabled on FreeBSD due to +//@ignore-target: freebsd use std::num::NonZero; use std::thread::available_parallelism; diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index e312feda7e62b..afcd66dd16e58 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -1,15 +1,11 @@ -use std::ffi::OsString; +use std::ffi::{CStr, OsString}; use std::path::PathBuf; use std::{fs, io}; -use super::miri_extern; +use super::{into_c_string, miri_extern}; pub fn host_to_target_path(path: OsString) -> PathBuf { - use std::ffi::{CStr, CString}; - - // Once into_encoded_bytes is stable we can use it here. - // (Unstable features would need feature flags in each test...) - let path = CString::new(path.into_string().unwrap()).unwrap(); + let path = into_c_string(path); let mut out = Vec::with_capacity(1024); unsafe { @@ -32,6 +28,8 @@ pub fn tmp() -> PathBuf { /// Prepare: compute filename and make sure the file does not exist. pub fn prepare(filename: &str) -> PathBuf { + assert!(filename.starts_with("miri")); + let path = tmp().join(filename); // Clean the paths for robustness. fs::remove_file(&path).ok(); @@ -47,6 +45,8 @@ pub fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { /// Prepare directory: compute directory name and make sure it does not exist. pub fn prepare_dir(dirname: &str) -> PathBuf { + assert!(dirname.starts_with("miri")); + let path = tmp().join(&dirname); // Clean the directory for robustness. fs::remove_dir_all(&path).ok(); diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index f855f115e43dc..3fe655350776c 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![allow(unused_imports)] +use std::ffi::{CString, OsString}; + #[macro_use] mod macros; @@ -12,6 +14,11 @@ pub use self::fs::*; pub use self::io::*; pub use self::miri_extern::*; +/// Return a `CString` version of the given string. +pub fn into_c_string(str: impl Into) -> CString { + CString::new(str.into().into_string().unwrap()).unwrap() +} + pub fn run_provenance_gc() { // SAFETY: No preconditions. The GC is fine to run at any time. unsafe { miri_run_provenance_gc() } diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 3235627e9ca5f..c9e59f6f7eaa7 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -75,15 +75,21 @@ //@ [AIX] compile-flags: --target powerpc64-ibm-aix //@ [AIX] needs-llvm-components: powerpc +// NOTE: in clang <= 23 the Complex is passed incorrectly. +// See https://github.com/llvm/llvm-project/issues/212109. +//@ revisions: MIPS64 MIPS64EL +//@ [MIPS64] compile-flags: --target mips64-unknown-linux-gnuabi64 +//@ [MIPS64] needs-llvm-components: mips +//@ [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64 +//@ [MIPS64EL] needs-llvm-components: mips + // FIXME: the below revisions are deliberately disabled for now. // revisions: POWERPC // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu // [POWERPC] needs-llvm-components: powerpc -// revisions: MIPS64EL MIPS -// [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64 -// [MIPS64EL] needs-llvm-components: mips +// revisions: MIPS // [MIPS] compile-flags: --target mips-unknown-linux-gnu // [MIPS] needs-llvm-components: mips @@ -143,7 +149,8 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // I686: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // LOONGARCH64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) - // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) + // MIPS64: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) + // MIPS64EL: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) // MIPS: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}}) // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // POWERPC64: define{{.*}} { float, float } @cplx_f32({ float, float } %0) @@ -177,7 +184,8 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // I686: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) - // MIPS64EL: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) + // MIPS64: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) + // MIPS64EL: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) // MIPS: define{{.*}} { double, double } @cplx_f64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) // POWERPC64: define{{.*}} { double, double } @cplx_f64({ double, double } %0) @@ -202,6 +210,8 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { pub extern "C" fn cplx_f128(x: Complex) -> Complex { // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) + // MIPS64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, i32 %0, { fp128, fp128 } %1) + // MIPS64EL: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, i32 %0, { fp128, fp128 } %1) // SPARC: define{{.*}} inreg { fp128, fp128 } @cplx_f128(ptr {{.*}} dereferenceable(32) {{.*}}) // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) @@ -226,7 +236,8 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // I686: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) - // MIPS64EL: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) + // MIPS64: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) + // MIPS64EL: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) // MIPS: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) // NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // POWERPC64: define{{.*}} { i8, i8 } @cplx_i8({ i8, i8 } %0) @@ -260,7 +271,8 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // I686: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) - // MIPS64EL: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) + // MIPS64: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) + // MIPS64EL: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) // MIPS: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) // NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) // POWERPC64: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) @@ -294,7 +306,8 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // I686: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // MIPS64EL: define{{.*}} { i32, i32 } @cplx_i32(i64 {{.*}}) + // MIPS64: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) + // MIPS64EL: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) // MIPS: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) // POWERPC64: define{{.*}} { i32, i32 } @cplx_i32({ i32, i32 } %0) @@ -328,7 +341,8 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) - // MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) + // MIPS64: define{{.*}} { i64, i64 } @cplx_i64(i128 {{.*}}) + // MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i128 {{.*}}) // MIPS: define{{.*}} { i64, i64 } @cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @cplx_i64({ i64, i64 } %0) @@ -367,7 +381,8 @@ pub extern "C" fn wrapper_cplx_i64( // I686: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) - // MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) + // MIPS64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i128 {{.*}}) + // MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i128 {{.*}}) // MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @wrapper_cplx_i64({ i64, i64 } %0) diff --git a/tests/codegen-llvm/direct-access-external-data.rs b/tests/codegen-llvm/direct-access-external-data.rs index a151bb6012e1e..43ee290d3a058 100644 --- a/tests/codegen-llvm/direct-access-external-data.rs +++ b/tests/codegen-llvm/direct-access-external-data.rs @@ -22,15 +22,6 @@ unsafe extern "C" { // When "linkage" is used, we generate an indirection global. // Check dso_local is still applied to the actual global. - // CHECK: @EXTERNAL = external - // DEFAULT-SAME: dso_local - // PIE-NOT: dso_local - // DIRECT-SAME: dso_local - // INDIRECT-NOT: dso_local - // CHECK-SAME: global i8 - #[linkage = "external"] - safe static EXTERNAL: *const u32; - // CHECK: @WEAK = extern_weak // DEFAULT-SAME: dso_local // PIE-NOT: dso_local @@ -44,6 +35,5 @@ unsafe extern "C" { #[no_mangle] pub fn refer() { core::hint::black_box(VAR); - core::hint::black_box(EXTERNAL); core::hint::black_box(WEAK); } diff --git a/tests/codegen-llvm/loongarch/direct-access-external-data.rs b/tests/codegen-llvm/loongarch/direct-access-external-data.rs index de495d7fe9a72..e5b7124a722e2 100644 --- a/tests/codegen-llvm/loongarch/direct-access-external-data.rs +++ b/tests/codegen-llvm/loongarch/direct-access-external-data.rs @@ -20,15 +20,6 @@ unsafe extern "C" { // When "linkage" is used, we generate an indirection global. // Check dso_local is still applied to the actual global. - // CHECK: @EXTERNAL = external - // DEFAULT-NOT: dso_local - // PIE-NOT: dso_local - // DIRECT-SAME: dso_local - // INDIRECT-NOT: dso_local - // CHECK-SAME: global i8 - #[linkage = "external"] - safe static EXTERNAL: *const u32; - // CHECK: @WEAK = extern_weak // DEFAULT-NOT: dso_local // PIE-NOT: dso_local @@ -42,6 +33,5 @@ unsafe extern "C" { #[no_mangle] pub fn refer() { core::hint::black_box(VAR); - core::hint::black_box(EXTERNAL); core::hint::black_box(WEAK); } diff --git a/tests/crashes/156101.rs b/tests/crashes/156101.rs deleted file mode 100644 index c95361fab2ecc..0000000000000 --- a/tests/crashes/156101.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #156101 -fn main() { - format_args!(concat!("𐏿", "{f:?#}")); -} diff --git a/tests/run-make/linkage-attr-on-static/bar.rs b/tests/run-make/linkage-attr-on-static/bar.rs index 68607cbb65721..cbab76ba6f403 100644 --- a/tests/run-make/linkage-attr-on-static/bar.rs +++ b/tests/run-make/linkage-attr-on-static/bar.rs @@ -1,7 +1,7 @@ #![feature(linkage)] #[no_mangle] -#[linkage = "external"] +#[linkage = "weak"] static BAZ: i32 = 21; #[link(name = "foo", kind = "static")] diff --git a/tests/run-make/linkage-attr-on-static/rmake.rs b/tests/run-make/linkage-attr-on-static/rmake.rs index cd85542e9587d..f1cf80a2b0c5a 100644 --- a/tests/run-make/linkage-attr-on-static/rmake.rs +++ b/tests/run-make/linkage-attr-on-static/rmake.rs @@ -5,6 +5,8 @@ //@ ignore-cross-compile // Reason: the compiled binary is executed +//@ ignore-i686-pc-windows-gnu weak symbols broken with MinGW linker +//@ ignore-x86_64-pc-windows-gnu weak symbols broken with MinGW linker use run_make_support::{build_native_static_lib, run, rustc}; diff --git a/tests/run-make/metrics-dir-crate-hash/lib.rs b/tests/run-make/metrics-dir-crate-hash/lib.rs new file mode 100644 index 0000000000000..e0673f64605f5 --- /dev/null +++ b/tests/run-make/metrics-dir-crate-hash/lib.rs @@ -0,0 +1,4 @@ +#![feature(ascii_char)] // random lib feature +#![feature(test_unstable_lint)] // random lang feature + +pub fn documented() {} diff --git a/tests/run-make/metrics-dir-crate-hash/main.rs b/tests/run-make/metrics-dir-crate-hash/main.rs new file mode 100644 index 0000000000000..844b362c9f179 --- /dev/null +++ b/tests/run-make/metrics-dir-crate-hash/main.rs @@ -0,0 +1,6 @@ +#![feature(ascii_char)] // random lib feature +#![feature(test_unstable_lint)] // random lang feature + +fn main() { + println!("foobar"); +} diff --git a/tests/run-make/metrics-dir-crate-hash/rmake.rs b/tests/run-make/metrics-dir-crate-hash/rmake.rs new file mode 100644 index 0000000000000..90e0b16859c04 --- /dev/null +++ b/tests/run-make/metrics-dir-crate-hash/rmake.rs @@ -0,0 +1,51 @@ +// Verifies that when a crate hash is required solely for a metrics dir, that +// crate hash is successfully generated, either by the fallback mechanism or +// metadata generation. +// See https://github.com/rust-lang/rust/issues/163426. + +//@ ignore-cross-compile + +use std::path::{Path, PathBuf}; + +use run_make_support::rfs::create_dir_all; +use run_make_support::{ + cwd, filename_contains, has_extension, run_in_tmpdir, rustc, rustdoc, shallow_find_files, +}; + +fn find_feature_usage_metrics>(dir: P) -> Vec { + shallow_find_files(dir, |path| { + filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") + }) +} + +fn main() { + let metrics_dir = "metrics-rustdoc"; + create_dir_all(&metrics_dir); + rustdoc() + .input("lib.rs") + .out_dir("doc") + .env("RUST_BACKTRACE", "short") + .arg(format!("-Zmetrics-dir={}", metrics_dir)) + .run() + .assert_stderr_not_contains("internal compiler error"); + assert_eq!( + find_feature_usage_metrics(&metrics_dir).len(), + 1, + "rustdoc should dump exactly one metrics file" + ); + + let metrics_dir = "metrics-rustc"; + create_dir_all(&metrics_dir); + rustc() + .input("main.rs") + .crate_type("bin") + .env("RUST_BACKTRACE", "short") + .arg(format!("-Zmetrics-dir={}", metrics_dir)) + .run() + .assert_stderr_not_contains("internal compiler error"); + assert_eq!( + find_feature_usage_metrics(&metrics_dir).len(), + 1, + "rustc should dump exactly one metrics file" + ); +} diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index ce787f83ade6d..8d699a665ae24 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -51,17 +51,6 @@ pub extern "C" fn public_naked_generic() -> u32 { naked_asm!("mov rax, {}", "ret", const T::COUNT) } -#[linkage = "external"] -extern "C" fn vanilla_external_linkage() -> u32 { - 42 -} - -#[unsafe(naked)] -#[linkage = "external"] -extern "C" fn naked_external_linkage() -> u32 { - naked_asm!("mov rax, 42", "ret") -} - #[cfg(not(windows))] #[linkage = "weak"] extern "C" fn vanilla_weak_linkage() -> u32 { diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs index 69317c872f895..1acbe11dc4ae4 100644 --- a/tests/run-make/naked-symbol-visibility/rmake.rs +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -29,9 +29,6 @@ fn main() { // -Zshare-generics. global_function(&rdylib, "public_naked_generic"); - global_function(&rdylib, "vanilla_external_linkage"); - global_function(&rdylib, "naked_external_linkage"); - // FIXME: make this work on windows (gnu and msvc). See the PR // https://github.com/rust-lang/rust/pull/128362 for some approaches // that don't work diff --git a/tests/ui/asm/naked-functions/naked-functions.rs b/tests/ui/asm/naked-functions/naked-functions.rs index d994b1151514a..e7da65a169074 100644 --- a/tests/ui/asm/naked-functions/naked-functions.rs +++ b/tests/ui/asm/naked-functions/naked-functions.rs @@ -224,7 +224,7 @@ pub extern "C" fn compatible_doc_attributes() { naked_asm!("", options(raw)); } -#[linkage = "external"] +#[linkage = "weak"] #[unsafe(naked)] pub extern "C" fn compatible_linkage() { naked_asm!("", options(raw)); diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs index 737ebf7f2b144..2ea4e0b5a395c 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs @@ -11,7 +11,7 @@ trait Test { //~^ ERROR cannot be used on required trait methods [unused_attributes] //~| WARN previously accepted fn method2(&self); - #[linkage = "common"] + #[linkage = "weak"] //~^ ERROR `linkage` attribute cannot be used on required trait methods fn method3(&self); #[track_caller] diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr index b5452ba2882c0..729231956337d 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr @@ -1,7 +1,7 @@ error: the `linkage` attribute cannot be used on required trait methods --> $DIR/codegen_attr_on_required_trait_method.rs:14:7 | -LL | #[linkage = "common"] +LL | #[linkage = "weak"] | ^^^^^^^ | = help: the `linkage` attribute can be applied to foreign functions, foreign statics, functions with a body, and statics diff --git a/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs new file mode 100644 index 0000000000000..30434569dbe73 --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs @@ -0,0 +1,34 @@ +// Test for handling of duplicated attributes within `cfg_attr`, in particular the suggestions of +// what to remove. + +#[inline] +#[inline] +//~^ WARN unused attribute +//~| WARN this was previously accepted +fn f1() {} + +#[deprecated] +#[deprecated] +//~^ ERROR multiple `deprecated` attributes +fn f2() {} + +#[inline] +#[cfg_attr(true, inline)] +//~^ WARN unused attribute +//~| WARN this was previously accepted +fn f3() {} + +#[deprecated] +#[cfg_attr(true, deprecated)] +//~^ ERROR multiple `deprecated` attributes +fn f4() {} + +#[inline] +#[deprecated] +#[cfg_attr(true, inline, deprecated)] +//~^ WARN unused attribute +//~| WARN this was previously accepted +//~| ERROR multiple `deprecated` attributes +fn f5() {} + +fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr new file mode 100644 index 0000000000000..c30bc26b22ce7 --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr @@ -0,0 +1,78 @@ +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:11:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:10:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:22:18 + | +LL | #[cfg_attr(true, deprecated)] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:21:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:28:26 + | +LL | #[cfg_attr(true, inline, deprecated)] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:27:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:5:1 + | +LL | #[inline] + | ^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:4:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: requested on the command line with `-W unused-attributes` + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:16:18 + | +LL | #[cfg_attr(true, inline)] + | ^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:15:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:28:18 + | +LL | #[cfg_attr(true, inline, deprecated)] + | ^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:26:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 3 previous errors; 3 warnings emitted + diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs index 907e1b966766f..508b449358964 100644 --- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs +++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs @@ -46,6 +46,9 @@ struct S12; //~| WARN previously accepted struct S13; +#[cfg_attr(true, cfg_attr(true, 3))] //~ ERROR expected identifier, found `3` +struct S14; + #[cfg_attr(true, inline())] //~ ERROR malformed `inline` attribute input fn f1() {} diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr index e901460a4eb88..048affd1c4191 100644 --- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr @@ -120,6 +120,19 @@ LL - #[cfg_attr(true)] LL + #[cfg_attr(predicate, attr1, attr2, ...)] | +error: expected identifier, found `3` + --> $DIR/cfg_attr-attr-syntax-validation.rs:49:33 + | +LL | #[cfg_attr(true, cfg_attr(true, 3))] + | ^ expected identifier + | + = note: for more information, visit +help: must be of the form + | +LL - #[cfg_attr(true, cfg_attr(true, 3))] +LL + #[cfg_attr(true, cfg_attr(predicate, attr1, attr2, ...))] + | + error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable --> $DIR/cfg_attr-attr-syntax-validation.rs:30:30 | @@ -157,7 +170,7 @@ LL | #[cfg_attr(true, link_section = "name")] | ++++++++ error[E0805]: malformed `inline` attribute input - --> $DIR/cfg_attr-attr-syntax-validation.rs:49:18 + --> $DIR/cfg_attr-attr-syntax-validation.rs:52:18 | LL | #[cfg_attr(true, inline())] | ^^^^^^-- @@ -185,7 +198,7 @@ LL | #[cfg_attr(true, link_section)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: requested on the command line with `-W unused-attributes` -error: aborting due to 13 previous errors; 1 warning emitted +error: aborting due to 14 previous errors; 1 warning emitted Some errors have detailed explanations: E0539, E0565, E0805. For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/fmt/format-args-concat-multibyte-suggestion.rs b/tests/ui/fmt/format-args-concat-multibyte-suggestion.rs new file mode 100644 index 0000000000000..51504607d2b61 --- /dev/null +++ b/tests/ui/fmt/format-args-concat-multibyte-suggestion.rs @@ -0,0 +1,38 @@ +// Regression test for #156101. +// +// When the format string comes from `concat!`, the parser's inner offsets are relative to the +// expanded string, not to the source, so they must not be turned into spans in the source. +// Before the fix, suggestions built from those offsets went wrong in two ways: +// +// - If the span landed inside a multibyte character, rendering the suggestion caused an ICE. +// The `ReorderFormatParameter` and `AddMissingColon` cases below are padded so that the span +// falls inside `é` (2 bytes) or `𐏿` (4 bytes). +// - If the span landed on unrelated source text, `UsePositional` suggested a bogus argument +// copied from that text. +// +// After the fix, no suggestion is emitted for these format strings. + +fn main() { + // The original reproducer from the issue. + format_args!(concat!("𐏿", "{f:?#}")); + //~^ ERROR invalid format string + + // `Suggestion::ReorderFormatParameter` + format_args!(concat!("é", "aaa{:?#}"), 1); + //~^ ERROR invalid format string + format_args!(concat!("𐏿", "a{:?#}"), 1); + //~^ ERROR invalid format string + + // `Suggestion::AddMissingColon` + format_args!(concat!("é", "aaaa{x?}")); + //~^ ERROR invalid format string + format_args!(concat!("𐏿", "aa{x?}")); + //~^ ERROR invalid format string + + // `Suggestion::UsePositional`: these did not ICE, but before the fix they suggested a bogus + // argument taken from the `concat!` invocation instead of from the format string. + format_args!(concat!("{}{a.b}", "")); + //~^ ERROR invalid format string + format_args!(concat!("{}{a.0}", "")); + //~^ ERROR invalid format string +} diff --git a/tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr b/tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr new file mode 100644 index 0000000000000..5eb8a4848a612 --- /dev/null +++ b/tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr @@ -0,0 +1,52 @@ +error: invalid format string: expected `}`, found `#` + --> $DIR/format-args-concat-multibyte-suggestion.rs:17:18 + | +LL | format_args!(concat!("𐏿", "{f:?#}")); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + +error: invalid format string: expected `}`, found `#` + --> $DIR/format-args-concat-multibyte-suggestion.rs:21:18 + | +LL | format_args!(concat!("é", "aaa{:?#}"), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + +error: invalid format string: expected `}`, found `#` + --> $DIR/format-args-concat-multibyte-suggestion.rs:23:18 + | +LL | format_args!(concat!("𐏿", "a{:?#}"), 1); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + +error: invalid format string: expected `}`, found `?` + --> $DIR/format-args-concat-multibyte-suggestion.rs:27:18 + | +LL | format_args!(concat!("é", "aaaa{x?}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `:` before `?` to format with `Debug` in format string + | + = note: to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `?` + --> $DIR/format-args-concat-multibyte-suggestion.rs:29:18 + | +LL | format_args!(concat!("𐏿", "aa{x?}")); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `:` before `?` to format with `Debug` in format string + | + = note: to print `{`, you can escape it using `{{` + +error: invalid format string: field access isn't supported + --> $DIR/format-args-concat-multibyte-suggestion.rs:34:18 + | +LL | format_args!(concat!("{}{a.b}", "")); + | ^^^^^^^^^^^^^^^^^^^^^^ not supported in format string + | + = note: consider moving this expression to a local variable and then using the local here instead + +error: invalid format string: tuple index access isn't supported + --> $DIR/format-args-concat-multibyte-suggestion.rs:36:18 + | +LL | format_args!(concat!("{}{a.0}", "")); + | ^^^^^^^^^^^^^^^^^^^^^^ not supported in format string + | + = note: consider moving this expression to a local variable and then using the local here instead + +error: aborting due to 7 previous errors + diff --git a/tests/ui/linkage-attr/auxiliary/def_external.rs b/tests/ui/linkage-attr/auxiliary/def_external.rs deleted file mode 100644 index 2300930e513af..0000000000000 --- a/tests/ui/linkage-attr/auxiliary/def_external.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![feature(linkage)] -#![crate_type = "lib"] - -#[linkage="external"] -pub static EXTERN: u32 = 0; diff --git a/tests/ui/linkage-attr/auxiliary/def_weak.rs b/tests/ui/linkage-attr/auxiliary/def_weak.rs new file mode 100644 index 0000000000000..a8940f73c5536 --- /dev/null +++ b/tests/ui/linkage-attr/auxiliary/def_weak.rs @@ -0,0 +1,5 @@ +#![feature(linkage)] +#![crate_type = "lib"] + +#[linkage = "weak"] +pub static WEAK: u32 = 0; diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs deleted file mode 100644 index 512616251c2f7..0000000000000 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ build-fail -//@ failure-status: 101 -//@ known-bug: #109681 -//@ ignore-wasm32 this appears to SIGABRT on wasm, not fail cleanly -//@ compile-flags: -Z verify-llvm-ir -//@ ignore-backends: gcc - -// This test verifies that we continue to hit the LLVM error for common linkage with non-zero -// initializers, since it generates invalid LLVM IR. -// Linkages are internal features marked as perma-unstable, so we don't need to fix the issue -// for now. -#![crate_type="lib"] -#![feature(linkage)] - -#[linkage = "common"] -#[no_mangle] -pub static TEST: bool = true; diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr b/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr deleted file mode 100644 index 93015bb2bac12..0000000000000 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr +++ /dev/null @@ -1,3 +0,0 @@ -'common' global must have a zero initializer! -ptr @TEST -rustc-LLVM ERROR: Broken module found, compilation aborted! diff --git a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index 0717a2d5a6c9e..e1ad2405b1df8 100644 --- a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -1,15 +1,9 @@ //@ run-pass //@ ignore-apple -//@ ignore-wasm32 common linkage not implemented right now +//@ ignore-wasm32 linkonce linkage not working right now #![feature(linkage)] -#[linkage = "external"] -pub static TEST2: bool = true; - -#[linkage = "internal"] -pub static TEST3: bool = true; - #[cfg(not(target_env = "msvc"))] #[linkage = "linkonce"] pub static TEST4: bool = true; diff --git a/tests/ui/linkage-attr/linkage-import.rs b/tests/ui/linkage-attr/linkage-import.rs index 838d1fc29a238..427a487ef1905 100644 --- a/tests/ui/linkage-attr/linkage-import.rs +++ b/tests/ui/linkage-attr/linkage-import.rs @@ -1,8 +1,10 @@ //@ build-pass -//@ aux-build:def_external.rs +//@ aux-build:def_weak.rs +//@ ignore-i686-pc-windows-gnu weak symbols broken with MinGW linker +//@ ignore-x86_64-pc-windows-gnu weak symbols broken with MinGW linker -extern crate def_external as dep; +extern crate def_weak as dep; fn main() { - println!("{:p}", &dep::EXTERN); + println!("{:p}", &dep::WEAK); } diff --git a/tests/ui/linkage-attr/linkage3.stderr b/tests/ui/linkage-attr/linkage3.stderr index aa25cd39ba291..616f80df09c8b 100644 --- a/tests/ui/linkage-attr/linkage3.stderr +++ b/tests/ui/linkage-attr/linkage3.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `linkage` attribute input LL | #[linkage = "foo"] | ^^^^^^^^^^----- | | - | valid arguments are `available_externally`, `common`, `extern_weak`, `external`, `internal`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` + | valid arguments are `available_externally`, `extern_weak`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` error: aborting due to 1 previous error diff --git a/tests/ui/linkage-attr/linkage4.rs b/tests/ui/linkage-attr/linkage4.rs index 3b935f9723dc1..25df81189d8f5 100644 --- a/tests/ui/linkage-attr/linkage4.rs +++ b/tests/ui/linkage-attr/linkage4.rs @@ -1,4 +1,4 @@ -#[linkage = "external"] +#[linkage = "weak"] static foo: isize = 0; //~^^ ERROR: the `linkage` attribute is experimental and not portable diff --git a/tests/ui/linkage-attr/linkage4.stderr b/tests/ui/linkage-attr/linkage4.stderr index a5970d100581a..2f04ceee5199d 100644 --- a/tests/ui/linkage-attr/linkage4.stderr +++ b/tests/ui/linkage-attr/linkage4.stderr @@ -1,7 +1,7 @@ error[E0658]: the `linkage` attribute is experimental and not portable across platforms --> $DIR/linkage4.rs:1:3 | -LL | #[linkage = "external"] +LL | #[linkage = "weak"] | ^^^^^^^ | = note: see issue #29603 for more information diff --git a/tests/ui/lint/inline-exported.rs b/tests/ui/lint/inline-exported.rs index 6a23cd58236ff..25ca5f0633079 100644 --- a/tests/ui/lint/inline-exported.rs +++ b/tests/ui/lint/inline-exported.rs @@ -17,12 +17,8 @@ fn export_name() {} #[inline] //~^ ERROR: `#[inline]` is ignored on externally exported functions -#[linkage = "external"] -fn external_linkage() {} +#[linkage = "weak"] +fn weak_linkage() {} #[inline] fn normal() {} - -#[inline] -#[linkage = "internal"] // not exported -fn internal_linkage() {} diff --git a/tests/ui/reborrow/reborrow-drop.rs b/tests/ui/reborrow/reborrow-drop.rs new file mode 100644 index 0000000000000..bc88bb79b9b4b --- /dev/null +++ b/tests/ui/reborrow/reborrow-drop.rs @@ -0,0 +1,14 @@ +//@ compile-flags: --crate-type=lib + +#![feature(reborrow)] + +use std::marker::Reborrow; + +struct MyMut<'a>(&'a mut ()); + +impl Reborrow for MyMut<'_> {} +//~^ ERROR the trait `Reborrow` cannot be implemented for this type; the type has a destructor [E0184] + +impl Drop for MyMut<'_> { + fn drop(&mut self) {} +} diff --git a/tests/ui/reborrow/reborrow-drop.stderr b/tests/ui/reborrow/reborrow-drop.stderr new file mode 100644 index 0000000000000..d35b36530d588 --- /dev/null +++ b/tests/ui/reborrow/reborrow-drop.stderr @@ -0,0 +1,15 @@ +error[E0184]: the trait `Reborrow` cannot be implemented for this type; the type has a destructor + --> $DIR/reborrow-drop.rs:9:1 + | +LL | impl Reborrow for MyMut<'_> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Reborrow` not allowed on types with destructors + | +note: destructor declared here + --> $DIR/reborrow-drop.rs:13:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0184`. diff --git a/tests/ui/sized-hierarchy/overflow.current.stderr b/tests/ui/sized-hierarchy/overflow.current.stderr index 9853be2a7f8e0..3573ee48db57c 100644 --- a/tests/ui/sized-hierarchy/overflow.current.stderr +++ b/tests/ui/sized-hierarchy/overflow.current.stderr @@ -1,11 +1,11 @@ error[E0275]: overflow evaluating the requirement `Element: MetaSized` - --> $DIR/overflow.rs:17:16 + --> $DIR/overflow.rs:15:16 | LL | struct Element(> as ParseTokens>::Output); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required for `Box` to implement `ParseTokens` - --> $DIR/overflow.rs:13:31 + --> $DIR/overflow.rs:11:31 | LL | impl ParseTokens for Box { | ------ ^^^^^^^^^^^ ^^^^^^ @@ -15,25 +15,25 @@ LL | impl ParseTokens for Box { = note: required for `Box>` to implement `ParseTokens` error[E0275]: overflow evaluating the requirement `Box: ParseTokens` - --> $DIR/overflow.rs:19:22 + --> $DIR/overflow.rs:18:22 | LL | impl ParseTokens for Element { | ^^^^^^^ | note: required for `Box>` to implement `ParseTokens` - --> $DIR/overflow.rs:13:31 + --> $DIR/overflow.rs:11:31 | LL | impl ParseTokens for Box { | ----------- ^^^^^^^^^^^ ^^^^^^ | | | unsatisfied trait bound introduced here note: required because it appears within the type `Element` - --> $DIR/overflow.rs:17:8 + --> $DIR/overflow.rs:15:8 | LL | struct Element(> as ParseTokens>::Output); | ^^^^^^^ note: required by a bound in `ParseTokens` - --> $DIR/overflow.rs:10:1 + --> $DIR/overflow.rs:8:1 | LL | / trait ParseTokens { LL | | type Output; diff --git a/tests/ui/sized-hierarchy/overflow.next.stderr b/tests/ui/sized-hierarchy/overflow.next.stderr new file mode 100644 index 0000000000000..69842a298f87f --- /dev/null +++ b/tests/ui/sized-hierarchy/overflow.next.stderr @@ -0,0 +1,29 @@ +error[E0275]: overflow evaluating the requirement `> as ParseTokens>::Output == _` + --> $DIR/overflow.rs:15:16 + | +LL | struct Element(> as ParseTokens>::Output); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating whether `> as ParseTokens>::Output` is well-formed + --> $DIR/overflow.rs:15:16 + | +LL | struct Element(> as ParseTokens>::Output); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `Element: MetaSized` + --> $DIR/overflow.rs:18:22 + | +LL | impl ParseTokens for Element { + | ^^^^^^^ + | +note: required by a bound in `ParseTokens` + --> $DIR/overflow.rs:8:1 + | +LL | / trait ParseTokens { +LL | | type Output; +LL | | } + | |_^ required by this bound in `ParseTokens` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/sized-hierarchy/overflow.rs b/tests/ui/sized-hierarchy/overflow.rs index 31c2ca8a49171..f69f56ef898e2 100644 --- a/tests/ui/sized-hierarchy/overflow.rs +++ b/tests/ui/sized-hierarchy/overflow.rs @@ -1,8 +1,6 @@ //@ compile-flags: --crate-type=lib //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) -//@[current] check-fail -//@[next] check-pass //@[next] compile-flags: -Znext-solver use std::marker::PhantomData; @@ -15,8 +13,9 @@ impl ParseTokens for Box { } struct Element(> as ParseTokens>::Output); -//[current]~^ ERROR: overflow +//~^ ERROR: overflow +//[next]~| ERROR: overflow impl ParseTokens for Element { -//[current]~^ ERROR: overflow +//~^ ERROR: overflow type Output = (); } diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr index 89b8b53c3f4d9..d6cd080cbfd0b 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr @@ -1,18 +1,18 @@ error[E0275]: overflow evaluating the requirement `u32: SendIndir>` - --> $DIR/only-one-coinductive-step-needed-trait.rs:24:5 + --> $DIR/only-one-coinductive-step-needed-trait.rs:25:5 | LL | is_send::>(); | ^^^^^^^^^^^^^^^^^^^^^ | note: required for `Foo` to implement `Send` - --> $DIR/only-one-coinductive-step-needed-trait.rs:17:35 + --> $DIR/only-one-coinductive-step-needed-trait.rs:18:35 | LL | unsafe impl>> Send for Foo {} | ----------------- ^^^^ ^^^^^^ | | | unsatisfied trait bound introduced here note: required by a bound in `is_send` - --> $DIR/only-one-coinductive-step-needed-trait.rs:22:15 + --> $DIR/only-one-coinductive-step-needed-trait.rs:23:15 | LL | fn is_send() {} | ^^^^ required by this bound in `is_send` diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr new file mode 100644 index 0000000000000..40ae37e495204 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `Foo: Send` + --> $DIR/only-one-coinductive-step-needed-trait.rs:25:15 + | +LL | is_send::>(); + | ^^^^^^^^ + | +note: required by a bound in `is_send` + --> $DIR/only-one-coinductive-step-needed-trait.rs:23:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs index 294156869284a..a8d23129fb87c 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs @@ -1,7 +1,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass // #136824 changed cycles to be coinductive if they have at least // one productive step, causing this test to pass with the new solver. @@ -12,6 +11,8 @@ // - `Foo: Send` cycle // // The old solver treats this cycle as inductive due to the `T: SendIndir` step. +// We later changed the new solver to temporarily be closer to the old solver +// again, so this now errors with both solvers. struct Foo(T); unsafe impl>> Send for Foo {} @@ -23,4 +24,5 @@ fn is_send() {} fn main() { is_send::>(); //[current]~^ ERROR overflow evaluating the requirement `u32: SendIndir>` + //[next]~^^ ERROR overflow evaluating the requirement `Foo: Send` } diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr index ec21987805869..1d7916f32b1db 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr @@ -1,11 +1,11 @@ error[E0275]: overflow evaluating the requirement `Foo: SendIndir` - --> $DIR/only-one-coinductive-step-needed.rs:17:15 + --> $DIR/only-one-coinductive-step-needed.rs:18:15 | LL | struct Foo( as Trait>::Assoc); | ^^^^^^^^^^^^^^^^^^^^^^^^ | note: required for `Foo` to implement `Trait` - --> $DIR/only-one-coinductive-step-needed.rs:26:20 + --> $DIR/only-one-coinductive-step-needed.rs:29:20 | LL | impl Trait for T { | --------- ^^^^^ ^ diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr new file mode 100644 index 0000000000000..071945d760345 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr @@ -0,0 +1,43 @@ +error[E0275]: overflow evaluating the requirement ` as Trait>::Assoc == _` + --> $DIR/only-one-coinductive-step-needed.rs:18:15 + | +LL | struct Foo( as Trait>::Assoc); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating whether ` as Trait>::Assoc` is well-formed + --> $DIR/only-one-coinductive-step-needed.rs:18:15 + | +LL | struct Foo( as Trait>::Assoc); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `Foo: Send` + --> $DIR/only-one-coinductive-step-needed.rs:36:15 + | +LL | is_send::>(); + | ^^^^^^^^ + | +note: required by a bound in `is_send` + --> $DIR/only-one-coinductive-step-needed.rs:33:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error[E0275]: overflow evaluating the requirement `Foo: Sized` + --> $DIR/only-one-coinductive-step-needed.rs:36:15 + | +LL | is_send::>(); + | ^^^^^^^^ + | +note: required by an implicit `Sized` bound in `is_send` + --> $DIR/only-one-coinductive-step-needed.rs:33:12 + | +LL | fn is_send() {} + | ^ required by the implicit `Sized` requirement on this type parameter in `is_send` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn is_send() {} + | ++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs index e41f7d7f3ceb3..9a13c0e945339 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs @@ -1,7 +1,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass // #136824 changed cycles to be coinductive if they have at least // one productive step, causing this test to pass with the new solver. @@ -12,10 +11,14 @@ // - `Foo: SendIndir`, via impl requires // - `Foo: Send` cycle // -// The old solver treats this cycle as inductive due to the `Foo: SendIndir` step. +// The old solver treats this cycle as inductive due to the `Foo: SendIndir` step. We've +// since changed the new solver to more closely match the old one here, also +// resulting in an inductive cycle for now. struct Foo( as Trait>::Assoc); -//[current]~^ ERROR overflow evaluating the requirement `Foo: SendIndir` +//[current]~^ ERROR: overflow evaluating the requirement `Foo: SendIndir` +//[next]~^^ ERROR: overflow evaluating the requirement ` as Trait>::Assoc == _` +//[next]~| ERROR: overflow evaluating whether ` as Trait>::Assoc` is well-formed trait SendIndir {} impl SendIndir for T {} @@ -31,4 +34,6 @@ fn is_send() {} fn main() { is_send::>(); + //[next]~^ ERROR: overflow evaluating the requirement `Foo: Send` + //[next]~| ERROR: overflow evaluating the requirement `Foo: Sized` } diff --git a/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr new file mode 100644 index 0000000000000..477b0ba993104 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr @@ -0,0 +1,26 @@ +error[E0275]: overflow evaluating the requirement `Foo<'_>: Send` + --> $DIR/cycle-requires-resolving-regions.rs:31:5 + | +LL | require::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_requires_resolving_regions`) +note: required for `Foo<'_>` to implement `Send` + --> $DIR/cycle-requires-resolving-regions.rs:23:17 + | +LL | unsafe impl<'a> Send for Foo<'a> + | ^^^^ ^^^^^^^ +LL | where +LL | Foo<'a>: Send, + | ---- unsatisfied trait bound introduced here + = note: 128 redundant requirements hidden + = note: required for `Foo<'static>` to implement `Send` +note: required by a bound in `require` + --> $DIR/cycle-requires-resolving-regions.rs:28:15 + | +LL | fn require() {} + | ^^^^ required by this bound in `require` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs new file mode 100644 index 0000000000000..2e1eaff4b9479 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// A test showcasing a behavior change in cycle detection between +// the old and new solver. The old solver detects cycles in both +// fulfill and in evaluate. The cycle detection in fulfillment +// does not resolve regions. Each occurance of the `Foo<'a>: Send` +// impl creates a fresh inference variable even if it is later +// constrainted to `'static`. This means we never detect a cycle. +// +// This is not an issue with builtin auto-trait impls as they don't +// create impl args instead, simply using the generic arguments of +// the self type directly. +// +// The new solver properly resolves regions. The old solver does resolve +// regions in `project`, so there we do detect cycles even if there are +// fresh region variables. + +struct Foo<'a>(&'a ()); + +unsafe impl<'a> Send for Foo<'a> +where + Foo<'a>: Send, +{} + +fn require() {} + +fn main() { + require::>(); + //[current]~^ ERROR: overflow evaluating the requirement `Foo<'_>: Send` +}