diff --git a/Cargo.lock b/Cargo.lock index 8ac10d7d4fb53..53cff99b4a199 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -994,9 +994,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.84+curl-8.17.0" +version = "0.4.87+curl-8.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc4294dc41b882eaff37973c2ec3ae203d0091341ee68fbadd1d06e0c18a73b" +checksum = "61a460380f0ef783703dcbe909107f39c162adeac050d73c850055118b5b6327" dependencies = [ "cc", "libc", @@ -2728,9 +2728,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" dependencies = [ "cc", "libc", diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index aa784153740a3..6ca6bf3e1f6dc 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -765,7 +765,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut bodies = std::mem::take(&mut self.bodies); let define_opaque = std::mem::take(&mut self.define_opaque); let trait_map = std::mem::take(&mut self.trait_map); - let delayed_lints = std::mem::take(&mut self.delayed_lints).into_boxed_slice(); + let delayed_lints = Steal::new(std::mem::take(&mut self.delayed_lints).into_boxed_slice()); #[cfg(debug_assertions)] for (id, attrs) in attrs.iter() { diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 09e22b6fc400e..80a1d9aa5defd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -113,7 +113,7 @@ pub fn parse_cfg_entry( else { return Err(cx.adcx().expected_identifier(meta.path().span())); }; - parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? + parse_name_value(name, meta.path().span(), a.as_name_value(), meta.span(), cx)? } }, MetaItemOrLitParser::Lit(lit) => match lit.kind { @@ -175,23 +175,16 @@ fn parse_cfg_entry_target( let mut result = ThinVec::new(); for sub_item in list.mixed() { // First, validate that this is a NameValue item - let Some(sub_item) = sub_item.meta_item() else { - cx.adcx().expected_name_value(sub_item.span(), None); - continue; - }; - let Some(nv) = sub_item.args().name_value() else { - cx.adcx().expected_name_value(sub_item.span(), None); + let Some((name, value)) = cx.expect_name_value(sub_item, sub_item.span(), None) else { continue; }; // Then, parse it as a name-value item - let Some(name) = sub_item.path().word_sym().filter(|s| !s.is_path_segment_keyword()) else { - return Err(cx.adcx().expected_identifier(sub_item.path().span())); - }; + if name.is_path_segment_keyword() { + return Err(cx.adcx().expected_identifier(name.span)); + } let name = Symbol::intern(&format!("target_{name}")); - if let Ok(cfg) = - parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx) - { + if let Ok(cfg) = parse_name_value(name, sub_item.span(), Some(value), sub_item.span(), cx) { result.push(cfg); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs index 40426654ea5b8..1a99e8ff1a6ff 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs @@ -11,11 +11,7 @@ impl SingleAttributeParser for CfiEncodingParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "encoding"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(name_value) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, Some(sym::cfi_encoding)); - return None; - }; + let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::cfi_encoding))?; let Some(value_str) = name_value.value_as_str() else { cx.adcx().expected_string_literal(name_value.value_span, None); diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 791b9de0f763e..a20813406b024 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -118,11 +118,7 @@ impl SingleAttributeParser for ExportNameParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -146,11 +142,7 @@ impl SingleAttributeParser for RustcObjcClassParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(classname) = nv.value_as_str() else { // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail // inside a standard library macro, but `cx.expected_string_literal` exposes too much. @@ -177,11 +169,7 @@ impl SingleAttributeParser for RustcObjcSelectorParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(methname) = nv.value_as_str() else { // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail // inside a standard library macro, but `cx.expected_string_literal` exposes too much. @@ -471,29 +459,20 @@ fn parse_tf_attribute( return features; } for item in list.mixed() { - let Some(name_value) = item.meta_item() else { - cx.adcx().expected_name_value(item.span(), Some(sym::enable)); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable)) + else { return features; }; // Validate name - let Some(name) = name_value.path().word_sym() else { - cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable)); - return features; - }; - if name != sym::enable { - cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable)); + if ident.name != sym::enable { + cx.adcx().expected_specific_argument(ident.span, &[sym::enable]); return features; } // Use value - let Some(name_value) = name_value.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::enable)); - return features; - }; - let Some(value_str) = name_value.value_as_str() else { - cx.adcx() - .expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); + let Some(value_str) = value.value_as_str() else { + cx.adcx().expected_string_literal(value.value_span, Some(value.value_as_lit())); return features; }; for feature in value_str.as_str().split(",") { @@ -592,14 +571,7 @@ impl SingleAttributeParser for SanitizeParser { let mut rtsan = None; for item in list.mixed() { - let Some(item) = item.meta_item() else { - cx.adcx().expected_name_value(item.span(), None); - continue; - }; - - let path = item.path().word_sym(); - let Some(value) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), path); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { continue; }; @@ -628,20 +600,20 @@ impl SingleAttributeParser for SanitizeParser { } }; - match path { - Some(sym::address) | Some(sym::kernel_address) => { + match ident.name { + sym::address | sym::kernel_address => { apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS) } - Some(sym::cfi) => apply(SanitizerSet::CFI), - Some(sym::kcfi) => apply(SanitizerSet::KCFI), - Some(sym::memory) => apply(SanitizerSet::MEMORY), - Some(sym::memtag) => apply(SanitizerSet::MEMTAG), - Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK), - Some(sym::thread) => apply(SanitizerSet::THREAD), - Some(sym::hwaddress) | Some(sym::kernel_hwaddress) => { + sym::cfi => apply(SanitizerSet::CFI), + sym::kcfi => apply(SanitizerSet::KCFI), + sym::memory => apply(SanitizerSet::MEMORY), + sym::memtag => apply(SanitizerSet::MEMTAG), + sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK), + sym::thread => apply(SanitizerSet::THREAD), + sym::hwaddress | sym::kernel_hwaddress => { apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS) } - Some(sym::realtime) => match value.value_as_str() { + sym::realtime => match value.value_as_str() { Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking), Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking), Some(sym::caller) => rtsan = Some(RtsanSetting::Caller), @@ -654,7 +626,7 @@ impl SingleAttributeParser for SanitizeParser { }, _ => { cx.adcx().expected_specific_argument_strings( - item.path().span(), + ident.span, &[ sym::address, sym::kernel_address, @@ -725,33 +697,25 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut errored = false; for item in meta_item_list.mixed() { - let Some(meta_item) = item.meta_item() else { - errored = true; - cx.adcx().expected_name_value(item.span(), None); - continue; - }; - - let Some(name_value_lit) = meta_item.args().name_value() else { - errored = true; - cx.adcx().expected_name_value(item.span(), None); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { continue; }; - let attrib_to_write = match meta_item.ident().map(|ident| ident.name) { - Some(sym::prefix_nops) => { + let attrib_to_write = match ident.name { + sym::prefix_nops => { // Duplicate prefixes are not allowed if prefix.is_some() { errored = true; - cx.adcx().duplicate_key(meta_item.path().span(), sym::prefix_nops); + cx.adcx().duplicate_key(ident.span, sym::prefix_nops); continue; } &mut prefix } - Some(sym::entry_nops) => { + sym::entry_nops => { // Duplicate entries are not allowed if entry.is_some() { errored = true; - cx.adcx().duplicate_key(meta_item.path().span(), sym::entry_nops); + cx.adcx().duplicate_key(ident.span, sym::entry_nops); continue; } &mut entry @@ -759,23 +723,23 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { _ => { errored = true; cx.adcx().expected_specific_argument( - meta_item.path().span(), + ident.span, &[sym::prefix_nops, sym::entry_nops], ); continue; } }; - let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else { + let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else { errored = true; - cx.adcx().expected_integer_literal(name_value_lit.value_span); + cx.adcx().expected_integer_literal(value.value_span); continue; }; let Ok(val) = val.get().try_into() else { errored = true; cx.adcx().expected_integer_literal_in_range( - name_value_lit.value_span, + value.value_span, u8::MIN as isize, u8::MAX as isize, ); diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index a0c2802ceee22..76fa2aed5c79c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -16,11 +16,7 @@ impl SingleAttributeParser for CrateNameParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let ArgParser::NameValue(n) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let n = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = n.value_as_str() else { cx.adcx().expected_string_literal(n.value_span, Some(n.value_as_lit())); @@ -47,11 +43,7 @@ impl CombineAttributeParser for CrateTypeParser { cx: &mut AcceptContext<'_, '_>, args: &ArgParser, ) -> impl IntoIterator { - let ArgParser::NameValue(n) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let n = cx.expect_name_value(args, cx.attr_span, None)?; let Some(crate_type) = n.value_as_str() else { cx.adcx().expected_string_literal(n.value_span, Some(n.value_as_lit())); @@ -95,11 +87,7 @@ impl SingleAttributeParser for RecursionLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::RecursionLimit { limit: cx.parse_limit_int(nv)?, @@ -117,11 +105,7 @@ impl SingleAttributeParser for MoveSizeLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::MoveSizeLimit { limit: cx.parse_limit_int(nv)?, @@ -140,11 +124,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::TypeLengthLimit { limit: cx.parse_limit_int(nv)?, @@ -162,11 +142,7 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::PatternComplexityLimit { limit: cx.parse_limit_int(nv)?, @@ -219,14 +195,7 @@ impl SingleAttributeParser for WindowsSubsystemParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let inner_span = cx.inner_span; - cx.adcx().expected_name_value( - args.span().unwrap_or(inner_span), - Some(sym::windows_subsystem), - ); - return None; - }; + let nv = cx.expect_name_value(args, cx.inner_span, Some(sym::windows_subsystem))?; let kind = match nv.value_as_str() { Some(sym::console) => WindowsSubsystemKind::Console, diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index e5287cbe01e3e..b77fdfadf8240 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -21,33 +21,24 @@ impl CombineAttributeParser for DebuggerViualizerParser { args: &ArgParser, ) -> impl IntoIterator { let single = cx.expect_single_element_list(args, cx.attr_span)?; - let Some(mi) = single.meta_item() else { - cx.adcx().expected_name_value(single.span(), None); - return None; - }; - let path = mi.path().word_sym(); - let visualizer_type = match path { - Some(sym::natvis_file) => DebuggerVisualizerType::Natvis, - Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter, + let (ident, args) = cx.expect_name_value(single, single.span(), None)?; + let visualizer_type = match ident.name { + sym::natvis_file => DebuggerVisualizerType::Natvis, + sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter, _ => { cx.adcx().expected_specific_argument( - mi.path().span(), + ident.span, &[sym::natvis_file, sym::gdb_script_file], ); return None; } }; - let Some(path) = mi.args().name_value() else { - cx.adcx().expected_name_value(single.span(), path); - return None; - }; - - let Some(path) = path.value_as_str() else { - cx.adcx().expected_string_literal(path.value_span, Some(path.value_as_lit())); + let Some(path) = args.value_as_str() else { + cx.adcx().expected_string_literal(args.value_span, Some(args.value_as_lit())); return None; }; - Some(DebugVisualizer { span: mi.span(), visualizer_type, path }) + Some(DebugVisualizer { span: ident.span.to(args.value_span), visualizer_type, path }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 27661569cd37a..3b6dd7a67ed17 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -18,15 +18,11 @@ fn get( cx.adcx().duplicate_key(param_span, name); return None; } - if let Some(v) = arg.name_value() { - if let Some(value_str) = v.value_as_ident() { - Some(value_str) - } else { - cx.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); - None - } + let v = cx.expect_name_value(arg, param_span, Some(name))?; + if let Some(value_str) = v.value_as_ident() { + Some(value_str) } else { - cx.adcx().expected_name_value(param_span, Some(name)); + cx.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); None } } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 85a3b4ad5bb9b..375eaba2844bb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -270,7 +270,7 @@ fn parse_directive_items<'p>( // But we don't assert its presence yet because we don't want to mention it // if someone does something like `#[diagnostic::on_unimplemented(doesnt_exist)]`. // That happens in the big `match` below. - let value: Option = match item.args().name_value() { + let value: Option = match item.args().as_name_value() { Some(nv) => Some(or_malformed!(nv.value_as_ident()?)), None => None, }; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 74fee3df0c524..c0b90c2c6d97f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -113,7 +113,7 @@ fn parse_keyword_and_attribute( attr_value: &mut Option<(Symbol, Span)>, attr_name: Symbol, ) { - let Some(nv) = args.name_value() else { + let Some(nv) = args.as_name_value() else { expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; @@ -397,7 +397,7 @@ impl DocParser { super::cfg::parse_name_value( name, sub_item.path().span(), - a.name_value(), + a.as_name_value(), sub_item.span(), cx, ) @@ -408,7 +408,7 @@ impl DocParser { // If `value` is `Some`, `a.name_value()` will always return // `Some` as well. value: value - .map(|v| (v, a.name_value().unwrap().value_span)), + .map(|v| (v, a.as_name_value().unwrap().value_span)), }) } } @@ -498,7 +498,7 @@ impl DocParser { } macro_rules! string_arg_and_crate_level { ($ident: ident) => {{ - let Some(nv) = args.name_value() else { + let Some(nv) = args.as_name_value() else { expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; @@ -605,7 +605,7 @@ impl DocParser { span, ); } - Some(sym::include) if let Some(nv) = args.name_value() => { + Some(sym::include) if let Some(nv) = args.as_name_value() => { let inner = match cx.attr_style { AttrStyle::Outer => "", AttrStyle::Inner => "!", diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 3ee3233fb1f76..0ca059bf4030b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -36,11 +36,7 @@ impl SingleAttributeParser for LinkNameParser { ); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -261,12 +257,11 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::name); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::name)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::name)) else { return false; }; let Some(link_name) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::name)); + cx.adcx().expected_string_literal(nv.args_span(), Some(nv.value_as_lit())); return false; }; @@ -288,12 +283,11 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::kind); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::kind)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::kind)) else { return true; }; let Some(link_kind) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::kind)); + cx.adcx().expected_string_literal(item.span(), Some(nv.value_as_lit())); return true; }; @@ -368,12 +362,11 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::modifiers); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::modifiers)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::modifiers)) else { return true; }; let Some(link_modifiers) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::modifiers)); + cx.adcx().expected_string_literal(item.span(), Some(nv.value_as_lit())); return true; }; *modifiers = Some((link_modifiers, nv.value_span)); @@ -410,12 +403,13 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::wasm_import_module); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::wasm_import_module)); + let Some(nv) = + cx.expect_name_value(item.args(), item.span(), Some(sym::wasm_import_module)) + else { return true; }; let Some(link_wasm_import_module) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::wasm_import_module)); + cx.adcx().expected_string_literal(item.span(), Some(nv.value_as_lit())); return true; }; *wasm_import_module = Some((link_wasm_import_module, item.span())); @@ -431,12 +425,12 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::import_name_type); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::import_name_type)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::import_name_type)) + else { return true; }; let Some(link_import_name_type) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::import_name_type)); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return true; }; if cx.sess().target.arch != Arch::X86 { @@ -503,11 +497,7 @@ impl SingleAttributeParser for LinkSectionParser { ); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -638,11 +628,7 @@ impl SingleAttributeParser for LinkageParser { ]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(name_value) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, Some(sym::linkage)); - return None; - }; + let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?; let Some(value) = name_value.value_as_str() else { cx.adcx() diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index 40ba852bc766b..cf0a28c2ea8e1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -13,11 +13,7 @@ impl SingleAttributeParser for PathParser { ); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(path) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index e324c3fee6808..0dfc666beac82 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -7,7 +7,7 @@ use rustc_span::{Span, Symbol, sym}; use crate::attributes::SingleAttributeParser; use crate::context::AcceptContext; -use crate::parser::ArgParser; +use crate::parser::{ArgParser, NameValueParser}; use crate::session_diagnostics; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::Allow; @@ -29,23 +29,23 @@ impl SingleAttributeParser for CustomMirParser { let mut failed = false; for item in list.mixed() { - let Some(meta_item) = item.meta_item() else { - cx.adcx().expected_name_value(item.span(), None); + let Some((path, arg)) = cx.expect_name_value(item, item.span(), None) else { failed = true; break; }; - if let Some(arg) = meta_item.word_is(sym::dialect) { - extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed); - } else if let Some(arg) = meta_item.word_is(sym::phase) { - extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed); - } else if let Some(..) = meta_item.path().word() { - cx.adcx().expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]); - failed = true; - } else { - cx.adcx().expected_name_value(meta_item.span(), None); - failed = true; - }; + match path.name { + sym::dialect => { + extract_value(cx, sym::dialect, arg, item.span(), &mut dialect, &mut failed) + } + sym::phase => { + extract_value(cx, sym::phase, arg, item.span(), &mut phase, &mut failed) + } + _ => { + cx.adcx().expected_specific_argument(item.span(), &[sym::dialect, sym::phase]); + failed = true; + } + } } let dialect = parse_dialect(cx, dialect, &mut failed); @@ -63,7 +63,7 @@ impl SingleAttributeParser for CustomMirParser { fn extract_value( cx: &mut AcceptContext<'_, '_>, key: Symbol, - arg: &ArgParser, + val: &NameValueParser, span: Span, out_val: &mut Option<(Symbol, Span)>, failed: &mut bool, @@ -74,12 +74,6 @@ fn extract_value( return; } - let Some(val) = arg.name_value() else { - cx.adcx().expected_name_value(span, Some(key)); - *failed = true; - return; - }; - let Some(value_sym) = val.value_as_str() else { cx.adcx().expected_string_literal(val.value_span, Some(val.value_as_lit())); *failed = true; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs index eb32ed09efa38..3706fec5e770b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs @@ -26,9 +26,9 @@ impl SingleAttributeParser for RustcAllocatorZeroedVariantParser { AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "function"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(name) = args.name_value().and_then(NameValueParser::value_as_str) else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); + let nv = cx.expect_name_value(args, cx.attr_span, None)?; + let Some(name) = nv.value_as_str() else { + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 36acd788b7f1a..835af97358ac4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -209,18 +209,17 @@ fn parse_cgu_fields( let mut kind = None::<(Symbol, Span)>; for arg in args.mixed() { - let Some(arg) = arg.meta_item() else { - cx.adcx().expected_name_value(args.span, None); + let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else { continue; }; - let res = match arg.ident().map(|i| i.name) { - Some(sym::cfg) => &mut cfg, - Some(sym::module) => &mut module, - Some(sym::kind) if accepts_kind => &mut kind, + let res = match ident.name { + sym::cfg => &mut cfg, + sym::module => &mut module, + sym::kind if accepts_kind => &mut kind, _ => { cx.adcx().expected_specific_argument( - arg.path().span(), + ident.span, if accepts_kind { &[sym::cfg, sym::module, sym::kind] } else { @@ -231,22 +230,17 @@ fn parse_cgu_fields( } }; - let Some(i) = arg.args().name_value() else { - cx.adcx().expected_name_value(arg.span(), None); - continue; - }; - - let Some(str) = i.value_as_str() else { - cx.adcx().expected_string_literal(i.value_span, Some(i.value_as_lit())); + let Some(str) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); continue; }; if res.is_some() { - cx.adcx().duplicate_key(arg.span(), arg.ident().unwrap().name); + cx.adcx().duplicate_key(ident.span.to(arg.args_span()), ident.name); continue; } - *res = Some((str, i.value_span)); + *res = Some((str, arg.value_span)); } let Some((cfg, _)) = cfg else { @@ -349,23 +343,15 @@ impl SingleAttributeParser for RustcDeprecatedSafe2024Parser { fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { let single = cx.expect_single_element_list(args, cx.attr_span)?; - let Some(arg) = single.meta_item() else { - cx.adcx().expected_name_value(single.span(), None); - return None; - }; - - let Some(args) = arg.word_is(sym::audit_that) else { - cx.adcx().expected_specific_argument(arg.span(), &[sym::audit_that]); - return None; - }; + let (path, arg) = cx.expect_name_value(single, cx.attr_span, None)?; - let Some(nv) = args.name_value() else { - cx.adcx().expected_name_value(arg.span(), Some(sym::audit_that)); + if path.name != sym::audit_that { + cx.adcx().expected_specific_argument(path.span, &[sym::audit_that]); return None; }; - let Some(suggestion) = nv.value_as_str() else { - cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + let Some(suggestion) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); return None; }; @@ -412,39 +398,33 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { let mut diverging_block_default = None::; for arg in list.mixed() { - let Some(meta) = arg.meta_item() else { - cx.adcx().expected_name_value(arg.span(), None); + let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else { continue; }; - let res = match meta.ident().map(|i| i.name) { - Some(sym::fallback) => &mut fallback, - Some(sym::diverging_block_default) => &mut diverging_block_default, + let res = match ident.name { + sym::fallback => &mut fallback, + sym::diverging_block_default => &mut diverging_block_default, _ => { cx.adcx().expected_specific_argument( - meta.path().span(), + ident.span, &[sym::fallback, sym::diverging_block_default], ); continue; } }; - let Some(nv) = meta.args().name_value() else { - cx.adcx().expected_name_value(meta.span(), None); - continue; - }; - - let Some(field) = nv.value_as_str() else { - cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + let Some(field) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); continue; }; if res.is_some() { - cx.adcx().duplicate_key(meta.span(), meta.ident().unwrap().name); + cx.adcx().duplicate_key(ident.span, ident.name); continue; } - *res = Some(Ident { name: field, span: nv.value_span }); + *res = Some(Ident { name: field, span: arg.value_span }); } let fallback = match fallback { @@ -562,11 +542,7 @@ impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) } } @@ -603,11 +579,7 @@ impl SingleAttributeParser for LangParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -701,13 +673,11 @@ impl CombineAttributeParser for RustcMirParser { sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness), sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow), sym::borrowck_graphviz_postflow => { - let Some(nv) = mi.args().name_value() else { - cx.adcx().expected_name_value( - mi.span(), - Some(sym::borrowck_graphviz_postflow), - ); - return None; - }; + let nv = cx.expect_name_value( + mi.args(), + mi.span(), + Some(sym::borrowck_graphviz_postflow), + )?; let Some(path) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, None); return None; @@ -721,13 +691,11 @@ impl CombineAttributeParser for RustcMirParser { } } sym::borrowck_graphviz_format => { - let Some(nv) = mi.args().name_value() else { - cx.adcx().expected_name_value( - mi.span(), - Some(sym::borrowck_graphviz_format), - ); - return None; - }; + let nv = cx.expect_name_value( + mi.args(), + mi.span(), + Some(sym::borrowck_graphviz_format), + )?; let Some(format) = nv.value_as_ident() else { cx.adcx().expected_identifier(nv.value_span); return None; @@ -814,10 +782,7 @@ impl CombineAttributeParser for RustcCleanParser { let mut cfg = None; for item in list.mixed() { - let Some((value, name)) = - item.meta_item().and_then(|m| Option::zip(m.args().name_value(), m.ident())) - else { - cx.adcx().expected_name_value(item.span(), None); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { continue; }; let value_span = value.value_span; @@ -825,11 +790,10 @@ impl CombineAttributeParser for RustcCleanParser { cx.adcx().expected_string_literal(value_span, None); continue; }; - match name.name { + match ident.name { sym::cfg if cfg.is_some() => { cx.adcx().duplicate_key(item.span(), sym::cfg); } - sym::cfg => { cfg = Some(value); } @@ -851,7 +815,7 @@ impl CombineAttributeParser for RustcCleanParser { } _ => { cx.adcx().expected_specific_argument( - name.span, + ident.span, &[sym::cfg, sym::except, sym::loaded_from_disk], ); } @@ -1047,11 +1011,7 @@ impl SingleAttributeParser for RustcDiagnosticItemParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(value) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -1106,11 +1066,7 @@ impl SingleAttributeParser for RustcReservationImplParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(args.span().unwrap_or(attr_span), None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(value_str) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); @@ -1137,11 +1093,7 @@ impl SingleAttributeParser for RustcDocPrimitiveParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "primitive name"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let span = cx.attr_span; - cx.adcx().expected_name_value(args.span().unwrap_or(span), None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(value_str) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index bfcb87eaf9d2a..d70c66dc18cf4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -102,9 +102,7 @@ impl AttributeParser for StabilityParser { template!(NameValueStr: "deprecation message"), |this, cx, args| { reject_outside_std!(cx); - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); + let Some(nv) = cx.expect_name_value(args, cx.attr_span, None) else { return; }; let Some(value_str) = nv.value_as_str() else { @@ -290,16 +288,19 @@ fn insert_value_into_option_or_error( ) -> Option<()> { if item.is_some() { cx.adcx().duplicate_key(name.span, name.name); - None - } else if let Some(v) = param.args().name_value() - && let Some(s) = v.value_as_str() - { - *item = Some(s); - Some(()) - } else { - cx.adcx().expected_name_value(param.span(), Some(name.name)); - None + return None; } + + let (_ident, arg) = cx.expect_name_value(param, param.span(), Some(name.name))?; + + let Some(s) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); + return None; + }; + + *item = Some(s); + + Some(()) } /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and @@ -409,7 +410,7 @@ pub(crate) fn parse_unstability( session_diagnostics::InvalidIssueString { span: param.span(), cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( - param.args().name_value().unwrap().value_span, + param.args().as_name_value().unwrap().value_span, err.kind(), ), }, diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 25031784c011a..4ed10b0ff1ac3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -73,20 +73,14 @@ impl SingleAttributeParser for ShouldPanicParser { } ArgParser::List(list) => { let single = cx.expect_single(list)?; - let Some(single) = single.meta_item() else { - cx.adcx().expected_name_value(single.span(), Some(sym::expected)); - return None; - }; - if !single.path().word_is(sym::expected) { + let (ident, arg) = + cx.expect_name_value(single, single.span(), Some(sym::expected))?; + if ident.name != sym::expected { cx.adcx().expected_specific_argument_strings(list.span, &[sym::expected]); return None; } - let Some(nv) = single.args().name_value() else { - cx.adcx().expected_name_value(single.span(), Some(sym::expected)); - return None; - }; - let Some(expected) = nv.value_as_str() else { - cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + let Some(expected) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); return None; }; Some(expected) @@ -104,14 +98,11 @@ impl SingleAttributeParser for ReexportTestHarnessMainParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let inner_span = cx.inner_span; - cx.adcx().expected_name_value( - args.span().unwrap_or(inner_span), - Some(sym::reexport_test_harness_main), - ); - return None; - }; + let nv = cx.expect_name_value( + args, + args.span().unwrap_or(cx.inner_span), + Some(sym::reexport_test_harness_main), + )?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); @@ -220,11 +211,7 @@ impl SingleAttributeParser for RustcTestMarkerParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "test_path"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(name_value) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, Some(sym::rustc_test_marker)); - return None; - }; + let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::rustc_test_marker))?; let Some(value_str) = name_value.value_as_str() else { cx.adcx().expected_string_literal(name_value.value_span, None); diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 39e56f47ab199..c83377a818f80 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -14,11 +14,7 @@ impl SingleAttributeParser for RustcMacroTransparencyParser { template!(NameValueStr: ["transparent", "semiopaque", "opaque"]); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; match nv.value_as_str() { Some(sym::transparent) => Some(Transparency::Transparent), Some(sym::semiopaque) => Some(Transparency::SemiOpaque), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 85fff6684ecd1..02a7aac9659f2 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -14,7 +14,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; // Glob imports to avoid big, bitrotty import lists use crate::attributes::allow_unstable::*; @@ -59,7 +59,10 @@ use crate::attributes::test_attrs::*; use crate::attributes::traits::*; use crate::attributes::transparency::*; use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs}; -use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, RefPathParser}; +use crate::parser::{ + ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, NameValueParser, + RefPathParser, +}; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, AttributeParseErrorSuggestions, ParsedDescription, @@ -490,7 +493,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list /// - /// This is a higher-level (and harder to misuse) wrapper over [`ArgParser::as_list`]. That + /// This is a higher-level (and harder to misuse) wrapper over [`ArgParser::as_list`] that /// allows using `?` when the attribute parsing function allows it. You may still want to use /// [`ArgParser::as_list`] for the following reasons: /// @@ -511,8 +514,8 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// Asserts that a [`MetaItemListParser`] contains a single element and returns it, or emits an /// error and returns `None`. /// - /// This is a higher-level (and harder to misuse) wrapper over [`MetaItemListParser::as_single`]. - /// That allows using `?` to early return. You may still want to use + /// This is a higher-level (and harder to misuse) wrapper over [`MetaItemListParser::as_single`], + /// that allows using `?` to early return. You may still want to use /// [`MetaItemListParser::as_single`] for the following reasons: /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). @@ -527,6 +530,123 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { } single } + + /// Asserts that a node is a name-value pair. + /// + /// Some examples: + /// + /// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a + /// name-value pair, where the name is a path (`clippy::cyclomatic_complexity`). You already + /// checked the path to get an `ArgParser`, so this method will effectively only assert that + /// the `= "100"` is there and returns it. + /// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair. `= "hello"` is returned. + /// - `#[serde(rename_all = "lowercase")]`: `rename_all = "lowercase"` is a name value pair, + /// where the name is an identifier (`rename_all`) and the value is a literal (`"lowercase"`). + /// This returns both the path and the value. + /// + /// `arg` must be a reference to any node that may contain a name-value pair, that is: + /// + /// - [`MetaItemOrLitParser`], + /// - [`MetaItemParser`], + /// - [`ArgParser`]. + /// + /// `name` can be set to `Some` for a nicer error message talking about the specific name that + /// was found lacking a value. + /// + /// This is a higher-level (and harder to misuse) wrapper over multiple `as_` methods in the + /// [`parser`][crate::parser] module. You may still want to use the lower-level methods for the + /// following reasons: + /// + /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). + /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + pub(crate) fn expect_name_value<'arg, Arg>( + &mut self, + arg: &'arg Arg, + span: Span, + name: Option, + ) -> Option> + where + Arg: ExpectNameValue, + { + arg.expect_name_value(self, span, name) + } +} + +pub(crate) trait ExpectNameValue { + type Output<'a> + where + Self: 'a; + + fn expect_name_value<'a, 'f, 'sess>( + &'a self, + cx: &mut AcceptContext<'f, 'sess>, + span: Span, + name: Option, + ) -> Option>; +} + +impl ExpectNameValue for MetaItemOrLitParser { + type Output<'a> = (Ident, &'a NameValueParser); + + fn expect_name_value<'a, 'f, 'sess>( + &'a self, + cx: &mut AcceptContext<'f, 'sess>, + span: Span, + name: Option, + ) -> Option> { + let Some(meta_item) = self.meta_item() else { + cx.adcx().expected_name_value(self.span(), name); + return None; + }; + + meta_item.expect_name_value(cx, span, name) + } +} + +impl ExpectNameValue for MetaItemParser { + type Output<'a> = (Ident, &'a NameValueParser); + + fn expect_name_value<'a, 'f, 'sess>( + &'a self, + cx: &mut AcceptContext<'f, 'sess>, + _span: Span, // Not needed: `MetaItemOrLitParser` carry its own span. + name: Option, + ) -> Option> { + let word = self.path().word(); + let arg = self.args().as_name_value(); + + if word.is_none() { + cx.adcx().expected_identifier(self.path().span()); + } + + if arg.is_none() { + cx.adcx().expected_name_value(self.span(), name); + } + + let Some((word, arg)) = word.zip(arg) else { + return None; + }; + + Some((word, arg)) + } +} + +impl ExpectNameValue for ArgParser { + type Output<'a> = &'a NameValueParser; + + fn expect_name_value<'a, 'f, 'sess>( + &'a self, + cx: &mut AcceptContext<'f, 'sess>, + span: Span, + name: Option, + ) -> Option> { + let Some(nv) = self.as_name_value() else { + cx.adcx().expected_name_value(span, name); + return None; + }; + + Some(nv) + } } impl<'f, 'sess> Deref for AcceptContext<'f, 'sess> { @@ -751,11 +871,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// Emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. - pub(crate) fn expected_name_value( - &mut self, - span: Span, - name: Option, - ) -> ErrorGuaranteed { + fn expected_name_value(&mut self, span: Span, name: Option) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index ce2367006128d..de505cc8c7ac8 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -192,7 +192,7 @@ impl ArgParser { /// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is /// there /// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair - pub fn name_value(&self) -> Option<&NameValueParser> { + pub fn as_name_value(&self) -> Option<&NameValueParser> { match self { Self::NameValue(n) => Some(n), Self::List(_) | Self::NoArgs => None, @@ -264,8 +264,9 @@ impl MetaItemOrLitParser { /// MetaItems consist of some path, and some args. The args could be empty. In other words: /// /// - `name` -> args are empty -/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the parentheses -/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the +/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the +/// parentheses +/// - `name = value`-> arg is [`name_value`](ArgParser::as_name_value), where the argument is the /// `= value` part /// /// The syntax of MetaItems can be found at diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 4f59033e56716..33bb4889ae22d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -4,6 +4,7 @@ use either::Either; use hir::{ExprKind, Param}; use rustc_abi::FieldIdx; use rustc_errors::{Applicability, Diag}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, BindingMode, ByRef, Node}; use rustc_middle::bug; @@ -1092,42 +1093,65 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut look_at_return = true; err.span_label(closure_span, "in this closure"); - // If the HIR node is a function or method call, get the DefId - // of the callee function or method, the span, and args of the call expr - let get_call_details = || { - let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else { - return None; + let closure_arg_has_fn_trait_bound = + |callee_def_id, input_index, generic_args: ty::GenericArgsRef<'tcx>| { + let sig = tcx.fn_sig(callee_def_id).instantiate(tcx, generic_args).skip_binder(); + let Some(input_ty): Option> = sig.inputs().get(input_index).copied() + else { + return false; + }; + + tcx.predicates_of(callee_def_id) + .instantiate(tcx, generic_args) + .predicates + .iter() + .any(|predicate| { + predicate.as_trait_clause().is_some_and(|trait_pred| { + trait_pred.polarity() == ty::PredicatePolarity::Positive + && tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) + == Some(ty::ClosureKind::Fn) + && trait_pred.self_ty().skip_binder().peel_refs() + == input_ty.peel_refs() + }) + }) }; - let typeck_results = tcx.typeck(def_id); + // If the HIR node is a function or method call, get the DefId + // of the callee function or method, the span, and argument info for the call expr. + let get_call_details = + || -> Option<(DefId, Span, usize, usize, ty::GenericArgsRef<'tcx>)> { + let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else { + return None; + }; - match kind { - hir::ExprKind::Call(expr, args) => { - if let Some(ty::FnDef(def_id, _)) = - typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind()) - { - Some((*def_id, expr.span, *args)) - } else { - None + let typeck_results = tcx.typeck(def_id); + + match kind { + hir::ExprKind::Call(expr, args) => { + if let Some(ty::FnDef(def_id, generic_args)) = + typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind()) + { + let arg_pos = args.iter().position(|arg| arg.hir_id == closure_id)?; + Some((*def_id, expr.span, arg_pos, arg_pos, generic_args)) + } else { + None + } } + hir::ExprKind::MethodCall(_, _, args, span) => { + let arg_pos = args.iter().position(|arg| arg.hir_id == closure_id)?; + let def_id = typeck_results.type_dependent_def_id(*hir_id)?; + let generic_args = typeck_results.node_args_opt(*hir_id)?; + Some((def_id, *span, arg_pos, arg_pos + 1, generic_args)) + } + _ => None, } - hir::ExprKind::MethodCall(_, _, args, span) => typeck_results - .type_dependent_def_id(*hir_id) - .map(|def_id| (def_id, *span, *args)), - _ => None, - } - }; + }; // If we can detect the expression to be a function or method call where the closure was // an argument, we point at the function or method definition argument... - if let Some((callee_def_id, call_span, call_args)) = get_call_details() { - let arg_pos = call_args - .iter() - .enumerate() - .filter(|(_, arg)| arg.hir_id == closure_id) - .map(|(pos, _)| pos) - .next(); - + if let Some((callee_def_id, call_span, arg_pos, input_index, generic_args)) = + get_call_details() + { let arg = match tcx.hir_get_if_local(callee_def_id) { Some( hir::Node::Item(hir::Item { @@ -1144,16 +1168,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .. }), ) => Some( - arg_pos - .and_then(|pos| { - sig.decl.inputs.get( - pos + if sig.decl.implicit_self().has_implicit_self() { - 1 - } else { - 0 - }, - ) - }) + sig.decl + .inputs + .get( + arg_pos + + if sig.decl.implicit_self().has_implicit_self() { 1 } else { 0 }, + ) .map(|arg| arg.span) .unwrap_or(ident.span), ), @@ -1163,6 +1183,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); err.span_label(call_span, "expects `Fn` instead of `FnMut`"); look_at_return = false; + } else if closure_arg_has_fn_trait_bound(callee_def_id, input_index, generic_args) { + // The callee is not local, so we cannot point at its argument declaration, but we + // can still explain that this call site expects an `Fn` closure. Avoid falling + // through to the enclosing function's return type, which is misleading in cases + // like `flat_map(|_| external::map(|_| ...))`. + err.span_label(call_span, "expects `Fn` instead of `FnMut`"); + look_at_return = false; } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 9163d639f972b..f9c91c3371516 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1085,8 +1085,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // Build a new closure where the return type is an owned value, instead of a ref. - let fn_sig_kind = - FnSigKind::default().set_safe(true).set_c_variadic(liberated_sig.c_variadic()); + let fn_sig_kind = FnSigKind::default() + .set_safety(hir::Safety::Safe) + .set_c_variadic(liberated_sig.c_variadic()); let closure_sig_as_fn_ptr_ty = Ty::new_fn_ptr( tcx, ty::Binder::dummy(tcx.mk_fn_sig( diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 3cffd862b9b98..065ed43be8cfe 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -2313,67 +2313,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.vector_extremum(a, b, ExtremumOperation::Min) } - #[cfg(feature = "master")] - pub fn vector_reduce_fmin(&mut self, src: RValue<'gcc>) -> RValue<'gcc> { - let vector_type = src.get_type().unqualified().dyncast_vector().expect("vector type"); - let element_count = vector_type.get_num_units(); - let mut acc = self - .context - .new_vector_access(self.location, src, self.context.new_rvalue_zero(self.int_type)) - .to_rvalue(); - for i in 1..element_count { - let elem = self - .context - .new_vector_access( - self.location, - src, - self.context.new_rvalue_from_int(self.int_type, i as _), - ) - .to_rvalue(); - let cmp = self.context.new_comparison(self.location, ComparisonOp::LessThan, acc, elem); - acc = self.select(cmp, acc, elem); - } - acc - } - - #[cfg(not(feature = "master"))] - pub fn vector_reduce_fmin(&mut self, _src: RValue<'gcc>) -> RValue<'gcc> { - unimplemented!(); - } - pub fn vector_maximum_number_nsz(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { self.vector_extremum(a, b, ExtremumOperation::Max) } - #[cfg(feature = "master")] - pub fn vector_reduce_fmax(&mut self, src: RValue<'gcc>) -> RValue<'gcc> { - let vector_type = src.get_type().unqualified().dyncast_vector().expect("vector type"); - let element_count = vector_type.get_num_units(); - let mut acc = self - .context - .new_vector_access(self.location, src, self.context.new_rvalue_zero(self.int_type)) - .to_rvalue(); - for i in 1..element_count { - let elem = self - .context - .new_vector_access( - self.location, - src, - self.context.new_rvalue_from_int(self.int_type, i as _), - ) - .to_rvalue(); - let cmp = - self.context.new_comparison(self.location, ComparisonOp::GreaterThan, acc, elem); - acc = self.select(cmp, acc, elem); - } - acc - } - - #[cfg(not(feature = "master"))] - pub fn vector_reduce_fmax(&mut self, _src: RValue<'gcc>) -> RValue<'gcc> { - unimplemented!(); - } - pub fn vector_select( &mut self, cond: RValue<'gcc>, diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 6fd19c4f82c37..bdb8316f3965d 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -1422,7 +1422,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); macro_rules! minmax_red { - ($name:ident: $int_red:ident, $float_red:ident) => { + ($name:ident: $int_red:ident) => { if name == sym::$name { require!( ret_ty == in_elem, @@ -1430,7 +1430,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); return match *in_elem.kind() { ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())), - ty::Float(_) => Ok(bx.$float_red(args[0].immediate())), _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, @@ -1444,8 +1443,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( }; } - minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin); - minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax); + minmax_red!(simd_reduce_min: vector_reduce_min); + minmax_red!(simd_reduce_max: vector_reduce_max); macro_rules! bitwise_red { ($name:ident : $op:expr, $boolean:expr) => { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 056a0763087a2..a61ec362e800d 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1668,12 +1668,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn vector_reduce_xor(&mut self, src: &'ll Value) -> &'ll Value { self.call_intrinsic("llvm.vector.reduce.xor", &[self.val_ty(src)], &[src]) } - pub(crate) fn vector_reduce_fmin(&mut self, src: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.vector.reduce.fmin", &[self.val_ty(src)], &[src]) - } - pub(crate) fn vector_reduce_fmax(&mut self, src: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.vector.reduce.fmax", &[self.val_ty(src)], &[src]) - } pub(crate) fn vector_reduce_min(&mut self, src: &'ll Value, is_signed: bool) -> &'ll Value { self.call_intrinsic( if is_signed { "llvm.vector.reduce.smin" } else { "llvm.vector.reduce.umin" }, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 94bd4a6ef76ef..81160b6c3fed5 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2922,7 +2922,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); macro_rules! minmax_red { - ($name:ident: $int_red:ident, $float_red:ident) => { + ($name:ident: $int_red:ident) => { if name == sym::$name { require!( ret_ty == in_elem, @@ -2931,7 +2931,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return match in_elem.kind() { ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)), ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)), - ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())), _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, @@ -2945,8 +2944,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }; } - minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin); - minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax); + // Currently no support for float due to . + minmax_red!(simd_reduce_min: vector_reduce_min); + minmax_red!(simd_reduce_max: vector_reduce_max); macro_rules! bitwise_red { ($name:ident : $red:ident, $boolean:expr) => { diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 3f12e857391b2..cd18aec994ba7 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -9,7 +9,7 @@ use ar_archive_writer::{ ArchiveKind, COFFShortExport, MachineTypes, NewArchiveMember, write_archive_to_stream, }; pub use ar_archive_writer::{DEFAULT_OBJECT_READER, ObjectReader}; -use object::read::archive::ArchiveFile; +use object::read::archive::{ArchiveFile, ArchiveKind as ObjectArchiveKind}; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; @@ -217,12 +217,10 @@ fn create_mingw_dll_import_lib( // able to control the *exact* spelling of each of the symbols that are being imported: // hence we don't want `dlltool` adding leading underscores automatically. let dlltool = find_binutils_dlltool(sess); - let temp_prefix = { - let mut path = PathBuf::from(&output_path); - path.pop(); - path.push(lib_name); - path - }; + // temp_prefix doesn't handle paths with spaces so + // use a relative path and set the current working directory + let cwd = output_path.parent().unwrap_or(output_path); + let temp_prefix = lib_name; // dlltool target architecture args from: // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 let (dlltool_target_arch, dlltool_target_bitness) = match &sess.target.arch { @@ -246,7 +244,8 @@ fn create_mingw_dll_import_lib( .arg(dlltool_target_bitness) .arg("--no-leading-underscore") .arg("--temp-prefix") - .arg(temp_prefix); + .arg(temp_prefix) + .current_dir(cwd); match dlltool_cmd.output() { Err(e) => { @@ -320,6 +319,50 @@ pub trait ArchiveBuilder { fn build(self: Box, output: &Path) -> bool; } +fn target_archive_format_to_object_kind(format: &str) -> Option { + match format { + "gnu" => Some(ObjectArchiveKind::Gnu), + "bsd" => Some(ObjectArchiveKind::Bsd), + "darwin" => Some(ObjectArchiveKind::Bsd64), + "coff" => Some(ObjectArchiveKind::Coff), + "aix_big" => Some(ObjectArchiveKind::AixBig), + _ => None, + } +} + +fn archive_kinds_compatible(actual: ObjectArchiveKind, expected: ObjectArchiveKind) -> bool { + if actual == expected { + return true; + } + matches!( + (actual, expected), + // An archive without long filenames or symbol table is detected as Unknown; + // this is compatible with any target format. + (ObjectArchiveKind::Unknown, _) + // 64-bit symbol table variants are compatible with their 32-bit counterparts + | (ObjectArchiveKind::Gnu64, ObjectArchiveKind::Gnu) + | (ObjectArchiveKind::Gnu, ObjectArchiveKind::Gnu64) + | (ObjectArchiveKind::Bsd64, ObjectArchiveKind::Bsd) + | (ObjectArchiveKind::Bsd, ObjectArchiveKind::Bsd64) + // GNU and COFF archives share the same magic and member header format; + // only the symbol table layout differs. + | (ObjectArchiveKind::Gnu, ObjectArchiveKind::Coff) + | (ObjectArchiveKind::Coff, ObjectArchiveKind::Gnu) + | (ObjectArchiveKind::Gnu64, ObjectArchiveKind::Coff) + ) +} + +fn archive_kind_display_name(kind: ObjectArchiveKind) -> String { + match kind { + ObjectArchiveKind::Gnu | ObjectArchiveKind::Gnu64 => "GNU".to_string(), + ObjectArchiveKind::Bsd => "BSD".to_string(), + ObjectArchiveKind::Bsd64 => "Darwin".to_string(), + ObjectArchiveKind::Coff => "COFF".to_string(), + ObjectArchiveKind::AixBig => "AIX big".to_string(), + _ => format!("{kind:?}"), + } +} + pub struct ArArchiveBuilderBuilder; impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { @@ -420,6 +463,19 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; let archive_index = self.src_archives.len(); + if let Some(expected_kind) = + target_archive_format_to_object_kind(&self.sess.target.archive_format) + { + let actual_kind = archive.kind(); + if !archive_kinds_compatible(actual_kind, expected_kind) { + self.sess.dcx().emit_warn(crate::errors::IncompatibleArchiveFormat { + path: archive_path.clone(), + actual: archive_kind_display_name(actual_kind), + expected: archive_kind_display_name(expected_kind), + }); + } + } + for entry in archive.members() { let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; let file_name = String::from_utf8(entry.name().to_vec()) @@ -482,9 +538,24 @@ impl<'a> ArArchiveBuilder<'a> { match entry { ArchiveEntry::FromArchive { archive_index, file_range } => { let src_archive = &self.src_archives[archive_index]; - - let data = &src_archive.1 - [file_range.0 as usize..file_range.0 as usize + file_range.1 as usize]; + let archive_data = &src_archive.1; + let start = file_range.0 as usize; + let end = start + file_range.1 as usize; + let Some(data) = archive_data.get(start..end) else { + return Err(io_error_context( + "invalid archive member", + io::Error::new( + io::ErrorKind::InvalidData, + format!( + "archive member at offset {start} with size {} \ + exceeds archive size {} in `{}`", + file_range.1, + archive_data.len(), + src_archive.0.display(), + ), + ), + )); + }; Box::new(data) as Box> } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 006b7f881ce23..c3afcd6f40333 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -677,6 +677,18 @@ pub(crate) struct UnknownArchiveKind<'a> { pub kind: &'a str, } +#[derive(Diagnostic)] +#[diag("archive `{$path}` was built as {$actual} format, but the target expects {$expected}")] +#[help( + "this often occurs when using BSD-format archive tools on a Linux target; \ + rebuild the archive with the correct format for the target platform" +)] +pub(crate) struct IncompatibleArchiveFormat { + pub path: PathBuf, + pub actual: String, + pub expected: String, +} + #[derive(Diagnostic)] #[diag("linking static libraries is not supported for BPF")] pub(crate) struct BpfStaticlibNotSupported; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 79a9b616cfcfa..8aa8ba09ed0c6 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -8,6 +8,7 @@ use std::assert_matches; use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_ast::{IntTy, UintTy}; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; @@ -21,9 +22,9 @@ use super::util::ensure_monomorphic_enough; use super::{ AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, Scalar, err_ub_format, err_unsup_format, interp_ok, - throw_inval, throw_ub, throw_ub_format, throw_unsup_format, + throw_inval, throw_ub, throw_ub_format, }; -use crate::interpret::Writeable; +use crate::interpret::{MPlaceTy, Writeable}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum MulAddType { @@ -56,6 +57,22 @@ pub(crate) enum MinMax { MaximumNumberNsz, } +/// Whether two types `T` and `U` are compatible when a value of type `T` is passed as a c-variadic +/// argument and read as a value of type `U`. +enum VarArgCompatible { + /// `T` and `U` are compatible, e.g. + /// + /// - They're the same type. + /// - One is `usize`/`isize`, the other an integer type of the same width + /// and sign on the current target. + /// - They are compatible pointer types (see the exact rules below). + Compatible, + /// `T` and `U` are definitely not compatible. + Incompatible, + /// `T` and `U` are corresponding signed and unsigned integer types. + CastIntTo { source_is_signed: bool }, +} + /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) { let path = crate::util::type_name(tcx, ty); @@ -739,18 +756,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(VaArgOutOfBounds); }; - // NOTE: In C some type conversions are allowed (e.g. casting between signed and - // unsigned integers). For now we require c-variadic arguments to be read with the - // exact type they were passed as. - if arg_mplace.layout.ty != dest.layout.ty { - throw_unsup_format!( - "va_arg type mismatch: requested `{}`, but next argument is `{}`", - dest.layout.ty, - arg_mplace.layout.ty - ); - } - // Copy the argument. - self.copy_op(&arg_mplace, dest)?; + // Error when the caller's argument is not c-variadic compatible with the type + // requested by the callee. + self.validate_c_variadic_argument(&arg_mplace, dest.layout)?; + + // Copy the argument, allowing a transmute and relying on the compatibility check + // rejecting conversions between types of different size. + self.copy_op_allow_transmute(&arg_mplace, dest)?; // Update the VaList pointer. let new_key = self.va_list_ptr(varargs); @@ -766,6 +778,130 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(true) } + /// Validate whether the value and type passed by the caller are compatible with the type + /// requested by the callee. Based on section 7.16.1.1 of the C23 specification. + /// + /// The callee requesting a value of a type is valid when that type is compatible with the type + /// provided by the caller (see `validate_c_variadic_compatible_ty`) and, if both types are + /// integers of the same size but different signedness, the passed value must be representable + /// in both types. + fn validate_c_variadic_argument( + &mut self, + arg_mplace: &MPlaceTy<'tcx, M::Provenance>, + callee_type: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx> { + let callee_ty = callee_type.ty; + let caller_ty = arg_mplace.layout.ty; + + // Identical types are clearly compatible. + if caller_ty == callee_ty { + return interp_ok(()); + } + + // Types of different sizes can never be compatible. + if arg_mplace.layout.size != callee_type.size { + throw_ub_format!( + "va_arg type mismatch: requested `{}` is incompatible with next argument of type `{}`", + callee_ty, + caller_ty, + ) + } + + match self.validate_c_variadic_compatible_ty(arg_mplace.layout.ty, callee_type.ty)? { + VarArgCompatible::Compatible => interp_ok(()), + VarArgCompatible::Incompatible => throw_ub_format!( + "va_arg type mismatch: requested `{}` is incompatible with next argument of type `{}`", + callee_ty, + caller_ty, + ), + VarArgCompatible::CastIntTo { source_is_signed } => { + // Check that the value can be represented in the target type. + let size = arg_mplace.layout.size; + let scalar = self.read_scalar(arg_mplace)?; + if scalar.to_int(size)? < 0 { + throw_ub_format!( + "va_arg value mismatch: value `{value}_{caller_ty}` cannot be represented by type `{callee_ty}`", + value = if source_is_signed { + scalar.to_int(size)?.to_string() + } else { + scalar.to_uint(size)?.to_string() + } + ) + } + + interp_ok(()) + } + } + } + + /// Check whether the caller and callee type are compatible for c-variadic calls. Further + /// validation of the argument value may be needed to detect all UB. + /// + /// Types `T` and `U` are compatible when: + /// + /// - `T` and `U` are the same type. + /// - `T` and `U` are integer types of the same size. + /// - `T` and `U` are both pointers, and their target types are compatible. + /// - `T` is a pointer to [`std::ffi::c_void`] and `U` is a pointer to [`i8`] or [`u8`], + /// or vice versa. + fn validate_c_variadic_compatible_ty( + &mut self, + caller_type: Ty<'tcx>, + callee_type: Ty<'tcx>, + ) -> InterpResult<'tcx, VarArgCompatible> { + if caller_type == callee_type { + return interp_ok(VarArgCompatible::Compatible); + } + + if self.layout_of(caller_type)?.size != self.layout_of(callee_type)?.size { + return interp_ok(VarArgCompatible::Incompatible); + } + + // Any character type (`char`, `unsigned char` and `signed char`) is compatible with + // `void*`, so the signedness of `c_char` is irrelevant here. + let is_c_char = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(UintTy::U8) | ty::Int(IntTy::I8)); + + match (caller_type.kind(), callee_type.kind()) { + (ty::RawPtr(caller_target_ty, _), ty::RawPtr(callee_target_ty, _)) => { + // In C, types can be qualified by a combination of `const`, `volatile` and + // `restrict`. These properties are irrelevant for the ABI, and don't have an + // equivalent in rust. + + // Accept the cast if one type is pointer to void, and the other is a pointer to + // a character type (`char`, `unsigned char` and `signed char`). + if caller_target_ty.is_c_void(self.tcx.tcx) && is_c_char(*callee_target_ty) { + return interp_ok(VarArgCompatible::Compatible); + } + if callee_target_ty.is_c_void(self.tcx.tcx) && is_c_char(*caller_target_ty) { + return interp_ok(VarArgCompatible::Compatible); + } + + // Accept the cast if both types are pointers to compatible types. + match self + .validate_c_variadic_compatible_ty(*caller_target_ty, *callee_target_ty)? + { + VarArgCompatible::Incompatible => interp_ok(VarArgCompatible::Incompatible), + VarArgCompatible::Compatible => interp_ok(VarArgCompatible::Compatible), + VarArgCompatible::CastIntTo { source_is_signed: _ } => { + // The integer cast check is not needed when the value is behind a pointer. + interp_ok(VarArgCompatible::Compatible) + } + } + } + (ty::Int(_), ty::Uint(_)) => { + interp_ok(VarArgCompatible::CastIntTo { source_is_signed: true }) + } + (ty::Uint(_), ty::Int(_)) => { + interp_ok(VarArgCompatible::CastIntTo { source_is_signed: false }) + } + (ty::Int(_), ty::Int(_)) | (ty::Uint(_), ty::Uint(_)) => { + // E.g. cast between `usize` and `u64` on a 64-bit platform. + interp_ok(VarArgCompatible::Compatible) + } + _ => interp_ok(VarArgCompatible::Incompatible), + } + } + pub(super) fn eval_nondiverging_intrinsic( &mut self, intrinsic: &NonDivergingIntrinsic<'tcx>, diff --git a/compiler/rustc_data_structures/src/flat_map_in_place.rs b/compiler/rustc_data_structures/src/flat_map_in_place.rs index d1fdd999d36ae..9064428202a8b 100644 --- a/compiler/rustc_data_structures/src/flat_map_in_place.rs +++ b/compiler/rustc_data_structures/src/flat_map_in_place.rs @@ -67,8 +67,13 @@ impl FlatMapInPlace for V { } } -// A vec-like type must implement these operations to support `flat_map_in_place`. -pub trait FlatMapInPlaceVec { +/// A vec-like type must implement these operations to support `flat_map_in_place`. +/// +/// # Safety +/// +/// The memory safety of the unsafe block in `flat_map_in_place` relies on impls of this trait +/// implementing all the operations correctly. +pub unsafe trait FlatMapInPlaceVec { type Elem; fn len(&self) -> usize; @@ -78,7 +83,7 @@ pub trait FlatMapInPlaceVec { fn insert(&mut self, idx: usize, elem: Self::Elem); } -impl FlatMapInPlaceVec for Vec { +unsafe impl FlatMapInPlaceVec for Vec { type Elem = T; fn len(&self) -> usize { @@ -104,7 +109,7 @@ impl FlatMapInPlaceVec for Vec { } } -impl FlatMapInPlaceVec for ThinVec { +unsafe impl FlatMapInPlaceVec for ThinVec { type Elem = T; fn len(&self) -> usize { @@ -130,7 +135,7 @@ impl FlatMapInPlaceVec for ThinVec { } } -impl FlatMapInPlaceVec for SmallVec<[T; N]> { +unsafe impl FlatMapInPlaceVec for SmallVec<[T; N]> { type Elem = T; fn len(&self) -> usize { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 9f1a20355f7de..aa1023f0e98a9 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -18,6 +18,7 @@ pub use rustc_ast::{ }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_index::IndexVec; @@ -1636,7 +1637,7 @@ pub struct OwnerInfo<'hir> { /// WARNING: The delayed lints are not hashed as a part of the `OwnerInfo`, and therefore /// should only be accessed in `eval_always` queries. #[stable_hasher(ignore)] - pub delayed_lints: DelayedLints, + pub delayed_lints: Steal, } impl<'tcx> OwnerInfo<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index dbf5465ee18b3..689911000ea8c 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -217,16 +217,26 @@ fn ensure_all_fields_are_const_destruct<'tcx>( unreachable!() }; let field_ty = eff.trait_ref.self_ty(); - let diag = struct_span_code_err!( + let mut diag = struct_span_code_err!( tcx.dcx(), error.root_obligation.cause.span, E0367, "`{field_ty}` does not implement `[const] Destruct`", ) .with_span_note(impl_span, "required for this `Drop` impl"); - if field_ty.has_param() { - // FIXME: suggest adding `[const] Destruct` by teaching - // `suggest_restricting_param_bound` about const traits. + if field_ty.has_param() + && let Some(generics) = tcx.hir_node_by_def_id(impl_def_id).generics() + { + let destruct_def_id = tcx.lang_items().destruct_trait(); + ty::suggest_constraining_type_param( + tcx, + generics, + &mut diag, + &field_ty.to_string(), + "[const] Destruct", + destruct_def_id, + None, + ); } Err(diag.emit()) }) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a43e373415ab7..5096e5ef76f0d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3590,7 +3590,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let fn_sig_kind = FnSigKind::default() .set_abi(abi) - .set_safe(safety.is_safe()) + .set_safety(safety) .set_c_variadic(decl.fn_decl_kind.c_variadic()); let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, fn_sig_kind); let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index d8240a0c9d70e..5a241ca846c3e 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -723,7 +723,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_sig = expected_sig.sig.map_bound(|sig| { let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) - .set_safe(true) + .set_safety(hir::Safety::Safe) .set_c_variadic(sig.c_variadic()); self.tcx.mk_fn_sig(sig.inputs().iter().cloned(), sig.output(), fn_sig_kind) }); @@ -860,7 +860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) - .set_safe(true) + .set_safety(hir::Safety::Safe) .set_c_variadic(expected_sigs.liberated_sig.c_variadic()); expected_sigs.liberated_sig = self.tcx.mk_fn_sig(inputs, supplied_output_ty, fn_sig_kind); @@ -935,7 +935,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) - .set_safe(true) + .set_safety(hir::Safety::Safe) .set_c_variadic(decl.c_variadic()); let result = ty::Binder::bind_with_vars( self.tcx.mk_fn_sig(supplied_arguments, supplied_return, fn_sig_kind), @@ -1098,7 +1098,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) - .set_safe(true) + .set_safety(hir::Safety::Safe) .set_c_variadic(decl.c_variadic()); let result = ty::Binder::dummy(self.tcx.mk_fn_sig(supplied_arguments, err_ty, fn_sig_kind)); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 842cb1f041311..38d899853cd5b 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1041,7 +1041,7 @@ impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for DiagCallback<'b, 'tcx> { pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { - for lint in delayed_lints { + for lint in delayed_lints.steal() { tcx.emit_node_span_lint( lint.lint_id.lint, lint.id, @@ -1117,7 +1117,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { let hir_items = tcx.hir_crate_items(()); for owner_id in hir_items.owners() { if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) - && !delayed_lints.is_empty() + && !delayed_lints.borrow().is_empty() { // Assert that delayed_lint_items also picked up this item to have lints. assert!(hir_items.delayed_lint_items().any(|i| i == owner_id)); diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index c24e433073cce..e0c6b61fbd949 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -8,6 +8,7 @@ use std::hash::Hash; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; +use rustc_hir::{ItemKind, Node, UseKind}; use rustc_macros::HashStable; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; @@ -185,13 +186,20 @@ impl EffectiveVisibilities { if !is_impl && tcx.trait_impl_of_assoc(def_id.to_def_id()).is_none() { let nominal_vis = tcx.visibility(def_id); if ev.reachable.greater_than(nominal_vis, tcx) { - span_bug!( - span, - "{:?}: reachable {:?} > nominal {:?}", - def_id, - ev.reachable, - nominal_vis, - ); + if let Node::Item(item) = tcx.hir_node_by_def_id(def_id) + && let ItemKind::Use(_, UseKind::Glob) = item.kind + { + // Glob import visibilities can be increased by other + // more public glob imports in cases of ambiguity. + } else { + span_bug!( + span, + "{:?}: reachable {:?} > nominal {:?}", + def_id, + ev.reachable, + nominal_vis, + ); + } } } } @@ -207,12 +215,11 @@ impl EffectiveVisibilities { self.map.get(&id) } - // FIXME: Share code with `fn update`. pub fn effective_vis_or_private( &mut self, id: Id, lazy_private_vis: impl FnOnce() -> Visibility, - ) -> &EffectiveVisibility { + ) -> &mut EffectiveVisibility { self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis())) } @@ -226,11 +233,7 @@ impl EffectiveVisibilities { tcx: TyCtxt<'_>, ) -> bool { let mut changed = false; - let mut current_effective_vis = self - .map - .get(&id) - .copied() - .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis())); + let current_effective_vis = self.effective_vis_or_private(id, lazy_private_vis); let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level); let mut calculated_effective_vis = inherited_effective_vis_at_prev_level; @@ -268,7 +271,6 @@ impl EffectiveVisibilities { } } - self.map.insert(id, current_effective_vis); changed } } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 7c6ab642b2736..f0c367beefd65 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -274,7 +274,7 @@ rustc_queries! { /// /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. - query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> { + query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx Steal> { desc { "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } // This query has to be `no_hash` and `eval_always`, // because it accesses `delayed_lints` which is not hashed as part of the HIR diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e1ef06ca7c713..e26eb13243762 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -46,7 +46,7 @@ use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; pub use rustc_type_ir::lift::Lift; -use rustc_type_ir::{CollectAndApply, FnSigKind, WithCachedTypeInfo, elaborate, search_graph}; +use rustc_type_ir::{CollectAndApply, WithCachedTypeInfo, elaborate, search_graph}; use tracing::{debug, instrument}; use crate::arena::Arena; @@ -66,11 +66,11 @@ use crate::traits; use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData, PredefinedOpaques}; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, - GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, Pattern, - PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, - Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, ValTree, ValTreeKind, - Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, FnSigKind, GenericArg, + GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, + Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, + PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, + ValTree, ValTreeKind, Visibility, }; impl<'tcx> rustc_type_ir::inherent::DefId> for DefId { @@ -83,50 +83,6 @@ impl<'tcx> rustc_type_ir::inherent::DefId> for DefId { } } -impl<'tcx> rustc_type_ir::inherent::FSigKind> for FnSigKind { - fn fn_sig_kind(self) -> Self { - self - } - - fn new(abi: ExternAbi, safety: hir::Safety, c_variadic: bool) -> Self { - FnSigKind::default().set_abi(abi).set_safe(safety.is_safe()).set_c_variadic(c_variadic) - } - - fn abi(self) -> ExternAbi { - self.abi() - } - - fn safety(self) -> hir::Safety { - if self.is_safe() { hir::Safety::Safe } else { hir::Safety::Unsafe } - } - - fn c_variadic(self) -> bool { - self.c_variadic() - } -} - -impl<'tcx> rustc_type_ir::inherent::Abi> for ExternAbi { - fn abi(self) -> Self { - self - } - - fn rust() -> Self { - ExternAbi::Rust - } - - fn is_rust(self) -> bool { - matches!(self, ExternAbi::Rust) - } - - fn pack_abi(self) -> u8 { - self.as_packed() - } - - fn unpack_abi(abi_index: u8) -> Self { - Self::from_packed(abi_index) - } -} - impl<'tcx> rustc_type_ir::inherent::Safety> for hir::Safety { fn safe() -> Self { hir::Safety::Safe @@ -1335,7 +1291,7 @@ impl<'tcx> TyCtxt<'tcx> { let caller_features = &self.body_codegen_attrs(caller).target_features; if self.is_target_feature_call_safe(&fun_features, &caller_features) { return Some(fun_sig.map_bound(|sig| ty::FnSig { - fn_sig_kind: fun_sig.fn_sig_kind().set_safe(true), + fn_sig_kind: fun_sig.fn_sig_kind().set_safety(hir::Safety::Safe), ..sig })); } @@ -2104,7 +2060,10 @@ impl<'tcx> TyCtxt<'tcx> { assert!(sig.safety().is_safe()); Ty::new_fn_ptr( self, - sig.map_bound(|sig| ty::FnSig { fn_sig_kind: sig.fn_sig_kind.set_safe(false), ..sig }), + sig.map_bound(|sig| ty::FnSig { + fn_sig_kind: sig.fn_sig_kind.set_safety(hir::Safety::Unsafe), + ..sig + }), ) } @@ -2113,7 +2072,10 @@ impl<'tcx> TyCtxt<'tcx> { /// unsafe. pub fn safe_to_unsafe_sig(self, sig: PolyFnSig<'tcx>) -> PolyFnSig<'tcx> { assert!(sig.safety().is_safe()); - sig.map_bound(|sig| ty::FnSig { fn_sig_kind: sig.fn_sig_kind.set_safe(false), ..sig }) + sig.map_bound(|sig| ty::FnSig { + fn_sig_kind: sig.fn_sig_kind.set_safety(hir::Safety::Unsafe), + ..sig + }) } /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name` @@ -2158,7 +2120,7 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_fn_sig( params, s.output(), - s.fn_sig_kind.set_safe(safety.is_safe()).set_abi(ExternAbi::Rust), + s.fn_sig_kind.set_safety(safety).set_abi(ExternAbi::Rust), ) }) } @@ -2417,7 +2379,12 @@ impl<'tcx> TyCtxt<'tcx> { // IntoIterator` instead of `I: Iterator`, and it doesn't have a slice // variant, because of the need to combine `inputs` and `output`. This // explains the lack of `_from_iter` suffix. - pub fn mk_fn_sig(self, inputs: I, output: I::Item, fn_sig_kind: FnSigKind) -> T::Output + pub fn mk_fn_sig( + self, + inputs: I, + output: I::Item, + fn_sig_kind: FnSigKind<'tcx>, + ) -> T::Output where I: IntoIterator, T: CollectAndApply, ty::FnSig<'tcx>>, @@ -2439,7 +2406,7 @@ impl<'tcx> TyCtxt<'tcx> { I: IntoIterator, T: CollectAndApply, ty::FnSig<'tcx>>, { - self.mk_fn_sig(inputs, output, FnSigKind::default().set_safe(safety.is_safe())) + self.mk_fn_sig(inputs, output, FnSigKind::default().set_safety(safety)) } /// `mk_fn_sig`, but with a safe Rust ABI, and no C-variadic argument. @@ -2448,7 +2415,7 @@ impl<'tcx> TyCtxt<'tcx> { I: IntoIterator, T: CollectAndApply, ty::FnSig<'tcx>>, { - self.mk_fn_sig(inputs, output, FnSigKind::default().set_safe(true)) + self.mk_fn_sig(inputs, output, FnSigKind::default().set_safety(hir::Safety::Safe)) } pub fn mk_poly_existential_predicates_from_iter(self, iter: I) -> T::Output diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index ccda7aeb910ca..6f6317d53d97b 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -2,7 +2,6 @@ use std::{debug_assert_matches, fmt}; -use rustc_abi::ExternAbi; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -10,9 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; -use rustc_type_ir::{ - CollectAndApply, FnSigKind, Interner, TypeFoldable, Unnormalized, search_graph, -}; +use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, Unnormalized, search_graph}; use crate::dep_graph::{DepKind, DepNodeIndex}; use crate::infer::canonical::CanonicalVarKinds; @@ -92,9 +89,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AllocId = crate::mir::interpret::AllocId; type Pat = Pattern<'tcx>; type PatList = &'tcx List>; - type FSigKind = FnSigKind; type Safety = hir::Safety; - type Abi = ExternAbi; type Const = ty::Const<'tcx>; type Consts = &'tcx List; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d8303a671c4a5..8c42d27572430 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -338,18 +338,39 @@ impl TyCtxt<'_> { self.parent(id.into().to_def_id()).expect_local() } - pub fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { - if descendant.krate != ancestor.krate { - return false; + /// Compare def-ids based on their position in def-id tree, ancestor def-ids are considered + /// larger than descendant def-ids, and two different def-ids are considered unordered if + /// neither of them is an ancestor of the other. + fn def_id_partial_cmp(self, lhs: DefId, rhs: DefId) -> Option { + // Def-ids from different crates are always unordered. + if lhs.krate != rhs.krate { + return None; } - while descendant != ancestor { - match self.opt_parent(descendant) { - Some(parent) => descendant = parent, - None => return false, + // Def-ids of parent nodes are always created before def-ids of child nodes + // and have a smaller index, so we only need to search in one direction, + // either from lhs to rhs, or vice versa. + let search = |mut start: DefId, finish: DefId, ord| { + while start.index != finish.index { + match self.opt_parent(start) { + Some(parent) => start.index = parent.index, + None => return None, + } } + Some(ord) + }; + match lhs.index.cmp(&rhs.index) { + Ordering::Equal => Some(Ordering::Equal), + Ordering::Less => search(rhs, lhs, Ordering::Greater), + Ordering::Greater => search(lhs, rhs, Ordering::Less), } - true + } + + pub fn is_descendant_of(self, descendant: DefId, ancestor: DefId) -> bool { + matches!( + self.def_id_partial_cmp(descendant, ancestor), + Some(Ordering::Less | Ordering::Equal) + ) } } @@ -391,15 +412,7 @@ impl> Visibility { (Visibility::Restricted(_), Visibility::Public) => Some(Ordering::Less), (Visibility::Restricted(lhs_id), Visibility::Restricted(rhs_id)) => { let (lhs_id, rhs_id) = (lhs_id.into(), rhs_id.into()); - if lhs_id == rhs_id { - Some(Ordering::Equal) - } else if tcx.is_descendant_of(rhs_id, lhs_id) { - Some(Ordering::Greater) - } else if tcx.is_descendant_of(lhs_id, rhs_id) { - Some(Ordering::Less) - } else { - None - } + tcx.def_id_partial_cmp(lhs_id, rhs_id) } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1b2332098665a..f997e49868ba8 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -751,7 +751,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if self.tcx().codegen_fn_attrs(def_id).safe_target_features { write!(self, "#[target_features] ")?; sig = sig.map_bound(|mut sig| { - sig.fn_sig_kind = sig.fn_sig_kind.set_safe(true); + sig.fn_sig_kind = sig.fn_sig_kind.set_safety(hir::Safety::Safe); sig }); } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f6d5d226683b3..29b784e837954 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -198,7 +198,6 @@ TrivialLiftImpls! { rustc_hir::Safety, rustc_middle::mir::ConstValue, rustc_type_ir::BoundConstness, - rustc_type_ir::FnSigKind, rustc_type_ir::PredicatePolarity, // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 61ff6cde91d74..bfdbc5b7b5850 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -37,7 +37,7 @@ pub type TypeAndMut<'tcx> = ir::TypeAndMut>; pub type AliasTy<'tcx> = ir::AliasTy>; pub type AliasTyKind<'tcx> = ir::AliasTyKind>; pub type FnSig<'tcx> = ir::FnSig>; -pub type FnSigKind = ir::FnSigKind; +pub type FnSigKind<'tcx> = ir::FnSigKind>; pub type Binder<'tcx, T> = ir::Binder, T>; pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>; pub type Unnormalized<'tcx, T> = ir::Unnormalized, T>; diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index eb56397cdca64..dc41bb1f687fe 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -314,7 +314,7 @@ impl RustcInternal for FnSig { ) -> Self::T<'tcx> { let fn_sig_kind = rustc_ty::FnSigKind::default() .set_abi(self.abi.internal(tables, tcx)) - .set_safe(self.safety == Safety::Safe) + .set_safety(self.safety.internal(tables, tcx)) .set_c_variadic(self.c_variadic); tcx.lift(rustc_ty::FnSig { inputs_and_output: tcx.mk_type_list(&self.inputs_and_output.internal(tables, tcx)), diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 3aa3408caddc8..4c4a51f7444bb 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -257,7 +257,7 @@ where // This internal type isn't publicly exposed, because it is an implementation detail. // But it's a public field of FnSig (which has a public mirror type), so allow conversions. -impl<'tcx> Stable<'tcx> for ty::FnSigKind { +impl<'tcx> Stable<'tcx> for ty::FnSigKind<'tcx> { type T = (bool /*c_variadic*/, crate::mir::Safety, crate::ty::Abi); fn stable<'cx>( &self, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 6310155154b95..1afbbef2eeaf8 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -93,7 +93,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ambiguity: CmCell::new(ambiguity), // External ambiguities always report the `AMBIGUOUS_GLOB_IMPORTS` lint at the moment. warn_ambiguity: CmCell::new(true), - vis: CmCell::new(vis), + initial_vis: vis, + ambiguity_vis_max: CmCell::new(None), + ambiguity_vis_min: CmCell::new(None), span, expansion, parent_module: Some(parent.to_module()), diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index cd7790f27b5f5..b5614106fe997 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -182,6 +182,10 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { parent_id.level(), tcx, ); + if let Some(max_vis_decl) = decl.ambiguity_vis_max.get() { + // Avoid the most visible import in an ambiguous glob set being reported as unused. + self.update_import(max_vis_decl, parent_id); + } } fn update_def( diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index a0f63a47f9b4e..5fae50041a02d 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -490,6 +490,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Some(Finalize { import, .. }) => import, }; + this.get_mut().maybe_push_glob_vs_glob_vis_ambiguity( + ident, + orig_ident_span, + decl, + import, + ); if let Some(&(innermost_decl, _)) = innermost_results.first() { // Found another solution, if the first one was "weak", report an error. @@ -779,6 +785,30 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ret.map_err(ControlFlow::Continue) } + fn maybe_push_glob_vs_glob_vis_ambiguity( + &mut self, + ident: IdentKey, + orig_ident_span: Span, + decl: Decl<'ra>, + import: Option, + ) { + let Some(import) = import else { return }; + let vis1 = self.import_decl_vis(decl, import); + let vis2 = self.import_decl_vis_ext(decl, import, true); + if vis1 != vis2 { + self.ambiguity_errors.push(AmbiguityError { + kind: AmbiguityKind::GlobVsGlob, + ambig_vis: Some((vis1, vis2)), + ident: ident.orig(orig_ident_span), + b1: decl.ambiguity_vis_max.get().unwrap_or(decl), + b2: decl.ambiguity_vis_min.get().unwrap_or(decl), + scope1: Scope::ModuleGlobs(decl.parent_module.unwrap(), None), + scope2: Scope::ModuleGlobs(decl.parent_module.unwrap(), None), + warning: Some(AmbiguityWarning::GlobImport), + }); + } + } + fn maybe_push_ambiguity( &mut self, ident: IdentKey, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index a3b80ae264da6..2695655278fb4 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -370,8 +370,17 @@ fn remove_same_import<'ra>(d1: Decl<'ra>, d2: Decl<'ra>) -> (Decl<'ra>, Decl<'ra impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn import_decl_vis(&self, decl: Decl<'ra>, import: ImportSummary) -> Visibility { + self.import_decl_vis_ext(decl, import, false) + } + + pub(crate) fn import_decl_vis_ext( + &self, + decl: Decl<'ra>, + import: ImportSummary, + min: bool, + ) -> Visibility { assert!(import.vis.is_accessible_from(import.nearest_parent_mod, self.tcx)); - let decl_vis = decl.vis(); + let decl_vis = if min { decl.min_vis() } else { decl.vis() }; if decl_vis.partial_cmp(import.vis, self.tcx) == Some(Ordering::Less) && decl_vis.is_accessible_from(import.nearest_parent_mod, self.tcx) && pub_use_of_private_extern_crate_hack(import, decl).is_none() @@ -409,7 +418,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ambiguity: CmCell::new(None), warn_ambiguity: CmCell::new(false), span: import.span, - vis: CmCell::new(vis.to_def_id()), + initial_vis: vis.to_def_id(), + ambiguity_vis_max: CmCell::new(None), + ambiguity_vis_min: CmCell::new(None), expansion: import.parent_scope.expansion, parent_module: Some(import.parent_scope.module), }) @@ -434,8 +445,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // - A glob decl is overwritten by its clone after setting ambiguity in it. // FIXME: avoid this by removing `warn_ambiguity`, or by triggering glob re-fetch // with the same decl in some way. - // - A glob decl is overwritten by a glob decl with larger visibility. - // FIXME: avoid this by updating this visibility in place. // - A glob decl is overwritten by a glob decl re-fetching an // overwritten decl from other module (the recursive case). // Here we are detecting all such re-fetches and overwrite old decls @@ -449,8 +458,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // FIXME: reenable the asserts when `warn_ambiguity` is removed (#149195). // assert_ne!(old_deep_decl, deep_decl); // assert!(old_deep_decl.is_glob_import()); - // FIXME: reenable the assert when visibility is updated in place. - // assert!(!deep_decl.is_glob_import()); + assert!(!deep_decl.is_glob_import()); if old_glob_decl.ambiguity.get().is_some() && glob_decl.ambiguity.get().is_none() { // Do not lose glob ambiguities when re-fetching the glob. glob_decl.ambiguity.set_unchecked(old_glob_decl.ambiguity.get()); @@ -470,12 +478,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // FIXME: remove this when `warn_ambiguity` is removed (#149195). self.arenas.alloc_decl((*old_glob_decl).clone()) } - } else if glob_decl.vis().greater_than(old_glob_decl.vis(), self.tcx) { - // We are glob-importing the same item but with greater visibility. + } else if let old_vis = old_glob_decl.vis() + && let vis = glob_decl.vis() + && old_vis != vis + { + // We are glob-importing the same item but with a different visibility. // All visibilities here are ordered because all of them are ancestors of `module`. - // FIXME: Update visibility in place, but without regressions - // (#152004, #151124, #152347). - glob_decl + if vis.greater_than(old_vis, self.tcx) { + old_glob_decl.ambiguity_vis_max.set_unchecked(Some(glob_decl)); + } else if let old_min_vis = old_glob_decl.min_vis() + && old_min_vis != vis + && old_min_vis.greater_than(vis, self.tcx) + { + old_glob_decl.ambiguity_vis_min.set_unchecked(Some(glob_decl)); + } + old_glob_decl } else if glob_decl.is_ambiguity_recursive() && !old_glob_decl.is_ambiguity_recursive() { // Overwriting a non-ambiguous glob import with an ambiguous glob import. old_glob_decl.ambiguity.set_unchecked(Some(glob_decl)); @@ -498,6 +515,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Result<(), Decl<'ra>> { assert!(!decl.warn_ambiguity.get()); assert!(decl.ambiguity.get().is_none()); + assert!(decl.ambiguity_vis_max.get().is_none()); + assert!(decl.ambiguity_vis_min.get().is_none()); let module = decl.parent_module.unwrap().expect_local(); assert!(self.is_accessible_from(decl.vis(), module.to_module())); let res = decl.res(); @@ -556,11 +575,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .resolution_or_default(module.to_module(), key, orig_ident_span) .borrow_mut_unchecked(); let old_decl = resolution.determined_decl(); + let old_vis = old_decl.map(|d| d.vis()); let t = f(self, resolution); if let Some(binding) = resolution.determined_decl() - && old_decl != Some(binding) + && (old_decl != Some(binding) || old_vis != Some(binding.vis())) { (binding, t, warn_ambiguity || old_decl.is_some()) } else { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 3b8f3c4a24c90..4d86da95e3fa8 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -929,7 +929,13 @@ struct DeclData<'ra> { warn_ambiguity: CmCell, expansion: LocalExpnId, span: Span, - vis: CmCell>, + initial_vis: Visibility, + /// If the declaration refers to an ambiguous glob set, then this is the most visible + /// declaration from the set, if its visibility is different from `initial_vis`. + ambiguity_vis_max: CmCell>>, + /// If the declaration refers to an ambiguous glob set, then this is the least visible + /// declaration from the set, if its visibility is different from `initial_vis`. + ambiguity_vis_min: CmCell>>, parent_module: Option>, } @@ -1049,7 +1055,13 @@ struct AmbiguityError<'ra> { impl<'ra> DeclData<'ra> { fn vis(&self) -> Visibility { - self.vis.get() + // Select the maximum visibility if there are multiple ambiguous glob imports. + self.ambiguity_vis_max.get().map(|d| d.vis()).unwrap_or_else(|| self.initial_vis) + } + + fn min_vis(&self) -> Visibility { + // Select the minimum visibility if there are multiple ambiguous glob imports. + self.ambiguity_vis_min.get().map(|d| d.vis()).unwrap_or_else(|| self.initial_vis) } fn res(&self) -> Res { @@ -1505,7 +1517,9 @@ impl<'ra> ResolverArenas<'ra> { kind: DeclKind::Def(res), ambiguity: CmCell::new(None), warn_ambiguity: CmCell::new(false), - vis: CmCell::new(vis), + initial_vis: vis, + ambiguity_vis_max: CmCell::new(None), + ambiguity_vis_min: CmCell::new(None), span, expansion, parent_module, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs index f7e1cbfac2635..0f7855c207c04 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs @@ -1,5 +1,6 @@ use crate::spec::{ - Arch, FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Arch, Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, + TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -15,6 +16,11 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { + // Enable the Cortex-A53 errata 843419 mitigation by default + pre_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-mfix-cortex-a53-843419"], + ), features: "+v8a,+outline-atomics".into(), // the AAPCS64 expects use of non-leaf frame pointers per // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs index 6ba5112342c3e..c48b7f7017de0 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs @@ -1,5 +1,6 @@ use crate::spec::{ - Arch, FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Arch, Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, + TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -29,6 +30,11 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { + // Enable the Cortex-A53 errata 843419 mitigation by default + pre_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-mfix-cortex-a53-843419"], + ), // the AAPCS64 expects use of non-leaf frame pointers per // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs index 9e4121d97eb8d..3c64948ad4a5f 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::Yes), &[ - "--target=wasm32-wasi-threads", + "--target=wasm32-linux-muslwali", "-Wl,--export-memory,", "-Wl,--shared-memory", "-Wl,--max-memory=1073741824", @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { ); Target { - llvm_target: "wasm32-wasi".into(), + llvm_target: "wasm32-linux-muslwali".into(), metadata: TargetMetadata { description: Some("WebAssembly Linux Interface with musl-libc".into()), tier: Some(3), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 0d6cd53dacab2..c46c042c886f7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -25,8 +25,8 @@ use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::{ - PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, PrintTraitRefExt as _, - with_forced_trimmed_paths, + PrintPolyTraitPredicateExt, PrintPolyTraitRefExt as _, PrintTraitPredicateExt as _, + PrintTraitRefExt as _, with_forced_trimmed_paths, }; use rustc_middle::ty::{ self, GenericArgKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -886,6 +886,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } } + } else if let ty::Param(param) = trait_ref.self_ty().skip_binder().kind() + && let Some(generics) = + self.tcx.hir_node_by_def_id(main_obligation.cause.body_id).generics() + { + let constraint = ty::print::with_no_trimmed_paths!(format!( + "[const] {}", + trait_ref.map_bound(|tr| tr.trait_ref).print_trait_sugared(), + )); + ty::suggest_constraining_type_param( + self.tcx, + generics, + &mut diag, + param.name.as_str(), + &constraint, + Some(trait_ref.def_id()), + None, + ); } diag } @@ -2708,7 +2725,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn predicate_can_apply( &self, param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitPredicate<'tcx>, + pred: impl Upcast, ty::Predicate<'tcx>> + TypeFoldable>, ) -> bool { struct ParamToVarFolder<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index eba4c7c6644ac..15fbd985d9630 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -1,4 +1,5 @@ use derive_where::derive_where; +use rustc_abi::ExternAbi; use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::solve::NoSolution; @@ -25,7 +26,7 @@ pub enum TypeError { Mismatch, PolarityMismatch(#[type_visitable(ignore)] ExpectedFound), SafetyMismatch(#[type_visitable(ignore)] ExpectedFound), - AbiMismatch(#[type_visitable(ignore)] ExpectedFound), + AbiMismatch(#[type_visitable(ignore)] ExpectedFound), Mutability, ArgumentMutability(usize), TupleSize(ExpectedFound), diff --git a/compiler/rustc_type_ir/src/generic_visit.rs b/compiler/rustc_type_ir/src/generic_visit.rs index 543397ff155b4..6669168443a4a 100644 --- a/compiler/rustc_type_ir/src/generic_visit.rs +++ b/compiler/rustc_type_ir/src/generic_visit.rs @@ -15,6 +15,8 @@ use rustc_index::{Idx, IndexVec}; use smallvec::SmallVec; use thin_vec::ThinVec; +use crate::Interner; + /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. /// @@ -210,4 +212,9 @@ trivial_impls!( rustc_hash::FxBuildHasher, crate::TypeFlags, crate::solve::GoalSource, + rustc_abi::ExternAbi, ); + +impl GenericTypeVisitable for crate::FnSigKind { + fn generic_visit_with(&self, _visitor: &mut V) {} +} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index a8757e045d170..3539215072ad9 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -205,42 +205,6 @@ pub trait Tys>: fn output(self) -> I::Ty; } -#[rust_analyzer::prefer_underscore_import] -pub trait FSigKind>: Copy + Debug + Hash + Eq { - /// The identity function. - fn fn_sig_kind(self) -> Self; - - /// Create a new FnSigKind with the given ABI, safety, and C-style variadic flag. - fn new(abi: I::Abi, safety: I::Safety, c_variadic: bool) -> Self; - - /// Returns the ABI. - fn abi(self) -> I::Abi; - - /// Returns the safety mode. - fn safety(self) -> I::Safety; - - /// Do the function arguments end with a C-style variadic argument? - fn c_variadic(self) -> bool; -} - -#[rust_analyzer::prefer_underscore_import] -pub trait Abi>: Copy + Debug + Hash + Eq { - /// The identity function. - fn abi(self) -> Self; - - /// The ABI `extern "Rust"`. - fn rust() -> I::Abi; - - /// Whether this ABI is `extern "Rust"`. - fn is_rust(self) -> bool; - - /// Pack the ABI into a small dense integer, so it can be stored as packed `FnSigKind` flags. - fn pack_abi(self) -> u8; - - /// Unpack the ABI from packed `FnSigKind` flags. - fn unpack_abi(abi_index: u8) -> Self; -} - #[rust_analyzer::prefer_underscore_import] pub trait Safety>: Copy + Debug + Hash + Eq { /// The `safe` safety mode. diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index f350e5da9654c..fba43b9cffbe2 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -145,9 +145,7 @@ pub trait Interner: + Eq + TypeVisitable + SliceLike; - type FSigKind: FSigKind; type Safety: Safety; - type Abi: Abi; // Kinds of consts type Const: Const; diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 61ac5acca7405..e136257a5b248 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::marker::PhantomData; use std::ops::Deref; use derive_where::derive_where; @@ -762,18 +763,31 @@ impl Eq for TypeAndMut {} /// Contains the packed non-type fields of a function signature. // FIXME(splat): add the splatted argument index as a u16 -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive_where(Copy, Clone, PartialEq, Eq, Hash; I: Interner)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -pub struct FnSigKind { +pub struct FnSigKind { /// Holds the c_variadic and safety bitflags, and 6 bits for the `ExternAbi` variant and unwind /// flag. + #[type_visitable(ignore)] + #[type_foldable(identity)] flags: u8, + #[type_visitable(ignore)] + #[type_foldable(identity)] + _marker: PhantomData I>, } -impl fmt::Debug for FnSigKind { +impl crate::lift::Lift for FnSigKind { + type Lifted = FnSigKind; + fn lift_to_interner(self, _cx: J) -> Option { + Some(FnSigKind { flags: self.flags, _marker: PhantomData }) + } +} + +impl fmt::Debug for FnSigKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_tuple("FnSigKind"); @@ -793,7 +807,7 @@ impl fmt::Debug for FnSigKind { } } -impl FnSigKind { +impl FnSigKind { /// Mask for the `ExternAbi` variant, including the unwind flag. const EXTERN_ABI_MASK: u8 = 0b111111; @@ -806,13 +820,21 @@ impl FnSigKind { /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic argument. /// To modify these flags, use the `set_*` methods, for readability. // FIXME: use Default instead when that trait is const stable. - pub const fn default() -> Self { - Self { flags: 0 }.set_abi(ExternAbi::Rust).set_safe(false).set_c_variadic(false) + pub fn default() -> Self { + Self { flags: 0, _marker: PhantomData } + .set_abi(ExternAbi::Rust) + .set_safety(I::Safety::unsafe_mode()) + .set_c_variadic(false) + } + + /// Create a new FnSigKind with the given ABI, safety, and C-style variadic flag. + pub fn new(abi: ExternAbi, safety: I::Safety, c_variadic: bool) -> Self { + Self::default().set_abi(abi).set_safety(safety).set_c_variadic(c_variadic) } /// Set the ABI, including the unwind flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_abi(mut self, abi: ExternAbi) -> Self { + pub fn set_abi(mut self, abi: ExternAbi) -> Self { let abi_index = abi.as_packed(); assert!(abi_index <= Self::EXTERN_ABI_MASK); @@ -824,8 +846,8 @@ impl FnSigKind { /// Set the safety flag, `true` is `Safe`. #[must_use = "this method does not modify the receiver"] - pub const fn set_safe(mut self, is_safe: bool) -> Self { - if is_safe { + pub fn set_safety(mut self, safety: I::Safety) -> Self { + if safety.is_safe() { self.flags |= Self::SAFE_FLAG; } else { self.flags &= !Self::SAFE_FLAG; @@ -836,7 +858,7 @@ impl FnSigKind { /// Set the C-style variadic argument flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_c_variadic(mut self, c_variadic: bool) -> Self { + pub fn set_c_variadic(mut self, c_variadic: bool) -> Self { if c_variadic { self.flags |= Self::C_VARIADIC_FLAG; } else { @@ -847,18 +869,23 @@ impl FnSigKind { } /// Get the ABI, including the unwind flag. - pub const fn abi(self) -> ExternAbi { + pub fn abi(self) -> ExternAbi { let abi_index = self.flags & Self::EXTERN_ABI_MASK; ExternAbi::from_packed(abi_index) } /// Get the safety flag. - pub const fn is_safe(self) -> bool { + pub fn is_safe(self) -> bool { self.flags & Self::SAFE_FLAG != 0 } + /// Returns the safety mode. + pub fn safety(self) -> I::Safety { + if self.is_safe() { I::Safety::safe() } else { I::Safety::unsafe_mode() } + } + /// Do the function arguments end with a C-style variadic argument? - pub const fn c_variadic(self) -> bool { + pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } } @@ -873,7 +900,7 @@ pub struct FnSig { pub inputs_and_output: I::Tys, #[type_visitable(ignore)] #[type_foldable(identity)] - pub fn_sig_kind: I::FSigKind, + pub fn_sig_kind: FnSigKind, } impl Eq for FnSig {} @@ -888,25 +915,18 @@ impl FnSig { } pub fn is_fn_trait_compatible(self) -> bool { - !self.c_variadic() && self.safety().is_safe() && self.abi().is_rust() + !self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust } - pub fn set_safe(self, is_safe: bool) -> Self { - Self { - fn_sig_kind: I::FSigKind::new( - self.abi(), - if is_safe { I::Safety::safe() } else { I::Safety::unsafe_mode() }, - self.c_variadic(), - ), - ..self - } + pub fn set_safety(self, safety: I::Safety) -> Self { + Self { fn_sig_kind: FnSigKind::new(self.abi(), safety, self.c_variadic()), ..self } } pub fn safety(self) -> I::Safety { self.fn_sig_kind.safety() } - pub fn abi(self) -> I::Abi { + pub fn abi(self) -> ExternAbi { self.fn_sig_kind.abi() } @@ -917,7 +937,7 @@ impl FnSig { pub fn dummy() -> Self { Self { inputs_and_output: Default::default(), - fn_sig_kind: I::FSigKind::new(I::Abi::rust(), I::Safety::safe(), false), + fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false), } } } @@ -943,7 +963,7 @@ impl ty::Binder> { self.map_bound(|fn_sig| fn_sig.output()) } - pub fn fn_sig_kind(self) -> I::FSigKind { + pub fn fn_sig_kind(self) -> FnSigKind { self.skip_binder().fn_sig_kind } @@ -955,7 +975,7 @@ impl ty::Binder> { self.skip_binder().safety() } - pub fn abi(self) -> I::Abi { + pub fn abi(self) -> ExternAbi { self.skip_binder().abi() } @@ -976,7 +996,7 @@ impl fmt::Debug for FnSig { let FnSig { inputs_and_output: _, fn_sig_kind } = sig; write!(f, "{}", fn_sig_kind.safety().prefix_str())?; - if !fn_sig_kind.abi().is_rust() { + if fn_sig_kind.abi() != ExternAbi::Rust { write!(f, "extern \"{:?}\" ", fn_sig_kind.abi())?; } @@ -1131,7 +1151,7 @@ impl ty::Binder> { pub struct FnHeader { #[type_visitable(ignore)] #[type_foldable(identity)] - pub fn_sig_kind: I::FSigKind, + pub fn_sig_kind: FnSigKind, } impl FnHeader { @@ -1143,12 +1163,12 @@ impl FnHeader { self.fn_sig_kind.safety() } - pub fn abi(self) -> I::Abi { + pub fn abi(self) -> ExternAbi { self.fn_sig_kind.abi() } pub fn dummy() -> Self { - Self { fn_sig_kind: I::FSigKind::new(I::Abi::rust(), I::Safety::safe(), false) } + Self { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false) } } } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 1ad5ed45e8b10..3b8ed0a15994d 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -9,7 +9,7 @@ use crate::data_structures::DelayedMap; use crate::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region}; use crate::inherent::*; use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use crate::{self as ty, Interner}; +use crate::{self as ty, FnSigKind, Interner}; /// A closure can be modeled as a struct that looks like: /// ```ignore (illustrative) @@ -367,7 +367,7 @@ pub struct CoroutineClosureSignature { /// Always safe, RustCall, non-c-variadic #[type_visitable(ignore)] #[type_foldable(identity)] - pub fn_sig_kind: I::FSigKind, + pub fn_sig_kind: FnSigKind, } impl Eq for CoroutineClosureSignature {} diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 0a35dc32ef19c..0a8b43622faef 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -390,11 +390,21 @@ impl<'f> VaList<'f> { /// /// # Safety /// - /// This function is only sound to call when there is another argument to read, and that - /// argument is a properly initialized value of the type `T`. + /// This function is safe to call only if all of the following conditions are satisfied: /// - /// Calling this function with an incompatible type, an invalid value, or when there - /// are no more variable arguments, is unsound. + /// - There is another c-variadic argument to read. + /// - The actual type of the argument `U` is compatible with `T` (as defined below). + /// - If `U` and `T` are both integer types, then the value passed by the caller must be + /// representable in both types. + /// + /// Types `T` and `U` are compatible when: + /// + /// - `T` and `U` are the same type. + /// - `T` and `U` are integer types of the same size. + /// - `T` and `U` are both pointers, and their target types are compatible. + /// - `T` is a pointer to [`c_void`] and `U` is a pointer to [`i8`] or [`u8`], or vice versa. + /// + /// [`c_void`]: core::ffi::c_void #[inline] // Avoid codegen when not used to help backends that don't support VaList. #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] pub const unsafe fn next_arg(&mut self) -> T { diff --git a/library/core/src/intrinsics/simd/mod.rs b/library/core/src/intrinsics/simd/mod.rs index 084d8a3f1f247..9311dcc9bd00e 100644 --- a/library/core/src/intrinsics/simd/mod.rs +++ b/library/core/src/intrinsics/simd/mod.rs @@ -520,7 +520,7 @@ pub unsafe fn simd_reduce_mul_unordered(x: T) -> U; /// Checks if all mask values are true. /// -/// `T` must be a vector of integer primitive types. +/// `T` must be a vector of integers. /// /// # Safety /// `x` must contain only `0` or `!0`. @@ -530,7 +530,7 @@ pub const unsafe fn simd_reduce_all(x: T) -> bool; /// Checks if any mask value is true. /// -/// `T` must be a vector of integer primitive types. +/// `T` must be a vector of integers. /// /// # Safety /// `x` must contain only `0` or `!0`. @@ -540,29 +540,25 @@ pub const unsafe fn simd_reduce_any(x: T) -> bool; /// Returns the maximum element of a vector. /// -/// `T` must be a vector of integers or floats. +/// `T` must be a vector of integers. /// /// `U` must be the element type of `T`. -/// -/// For floating-point values, uses IEEE-754 `maxNum`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_reduce_max(x: T) -> U; /// Returns the minimum element of a vector. /// -/// `T` must be a vector of integers or floats. +/// `T` must be a vector of integers. /// /// `U` must be the element type of `T`. -/// -/// For floating-point values, uses IEEE-754 `minNum`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_reduce_min(x: T) -> U; /// Logical "and"s all elements together. /// -/// `T` must be a vector of integers or floats. +/// `T` must be a vector of integers. /// /// `U` must be the element type of `T`. #[rustc_intrinsic] @@ -571,7 +567,7 @@ pub const unsafe fn simd_reduce_and(x: T) -> U; /// Logical "ors" all elements together. /// -/// `T` must be a vector of integers or floats. +/// `T` must be a vector of integers. /// /// `U` must be the element type of `T`. #[rustc_intrinsic] @@ -580,7 +576,7 @@ pub const unsafe fn simd_reduce_or(x: T) -> U; /// Logical "exclusive ors" all elements together. /// -/// `T` must be a vector of integers or floats. +/// `T` must be a vector of integers. /// /// `U` must be the element type of `T`. #[rustc_intrinsic] diff --git a/library/std_detect/src/detect/os/windows/aarch64.rs b/library/std_detect/src/detect/os/windows/aarch64.rs index 937f9f26eedc1..825e16bc2f864 100644 --- a/library/std_detect/src/detect/os/windows/aarch64.rs +++ b/library/std_detect/src/detect/os/windows/aarch64.rs @@ -31,8 +31,14 @@ pub(crate) fn detect_features() -> cache::Initializer { const PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE: u32 = 55; const PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE: u32 = 56; // const PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE: u32 = 57; - // const PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE: u32 = 58; - // const PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE: u32 = 59; + const PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE: u32 = 58; + const PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE: u32 = 59; + const PF_ARM_LSE2_AVAILABLE: u32 = 62; + const PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE: u32 = 64; + const PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE: u32 = 65; + const PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE: u32 = 66; + const PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE: u32 = 67; + const PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE: u32 = 68; unsafe extern "system" { fn IsProcessorFeaturePresent(ProcessorFeature: DWORD) -> BOOL; @@ -46,9 +52,11 @@ pub(crate) fn detect_features() -> cache::Initializer { } }; - // Some features may be supported on current CPU, - // but no way to detect it by OS API. - // Also, we require unsafe block for the extern "system" calls. + // Some features may be supported on the current CPU but have no + // detection path through the Win32 API; those report `false`. + // SAFETY: `IsProcessorFeaturePresent` is a Win32 entry point taking a + // `DWORD` by value and returning a `BOOL`. No pointer parameters, + // no out-parameters, no thread-safety constraints. unsafe { enable_feature( Feature::fp, @@ -112,6 +120,46 @@ pub(crate) fn detect_features() -> cache::Initializer { Feature::sve2_sm4, IsProcessorFeaturePresent(PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE) != FALSE, ); + enable_feature( + Feature::f32mm, + IsProcessorFeaturePresent(PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE) != FALSE, + ); + enable_feature( + Feature::f64mm, + IsProcessorFeaturePresent(PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE) != FALSE, + ); + enable_feature( + Feature::lse2, + IsProcessorFeaturePresent(PF_ARM_LSE2_AVAILABLE) != FALSE, + ); + enable_feature( + Feature::fp16, + IsProcessorFeaturePresent(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE) != FALSE, + ); + enable_feature( + Feature::i8mm, + IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE) != FALSE, + ); + enable_feature( + Feature::bf16, + IsProcessorFeaturePresent(PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE) != FALSE, + ); + // stdarch `sha3` is FEAT_SHA3 + FEAT_SHA512 together; Windows + // exposes them as two separate flags. + enable_feature( + Feature::sha3, + IsProcessorFeaturePresent(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE) != FALSE + && IsProcessorFeaturePresent(PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE) != FALSE, + ); + // No PF_ARM_RDM_* constant exists. Derive FEAT_RDM from FEAT_DotProd: + // DotProd is an optional v8.2-A feature only present on cores that + // implement at least v8.1-A; v8.1-A with AdvSIMD mandates FEAT_RDM + // (Arm ARM K.a §D17.2.91), and AdvSIMD is universal on Windows-on-ARM. + // Same inference shipped in .NET 10 (dotnet/runtime PR 109493). + enable_feature( + Feature::rdm, + IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != FALSE, + ); // PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE means aes, sha1, sha2 and // pmull support let crypto = diff --git a/library/std_detect/tests/cpu-detection.rs b/library/std_detect/tests/cpu-detection.rs index 0aad088af7de5..f0b276072108d 100644 --- a/library/std_detect/tests/cpu-detection.rs +++ b/library/std_detect/tests/cpu-detection.rs @@ -150,14 +150,22 @@ fn aarch64_linux() { fn aarch64_windows() { println!("asimd: {:?}", is_aarch64_feature_detected!("asimd")); println!("fp: {:?}", is_aarch64_feature_detected!("fp")); + println!("fp16: {:?}", is_aarch64_feature_detected!("fp16")); println!("crc: {:?}", is_aarch64_feature_detected!("crc")); println!("lse: {:?}", is_aarch64_feature_detected!("lse")); + println!("lse2: {:?}", is_aarch64_feature_detected!("lse2")); + println!("rdm: {:?}", is_aarch64_feature_detected!("rdm")); println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod")); + println!("i8mm: {:?}", is_aarch64_feature_detected!("i8mm")); + println!("bf16: {:?}", is_aarch64_feature_detected!("bf16")); println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv")); println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc")); println!("aes: {:?}", is_aarch64_feature_detected!("aes")); println!("pmull: {:?}", is_aarch64_feature_detected!("pmull")); println!("sha2: {:?}", is_aarch64_feature_detected!("sha2")); + println!("sha3: {:?}", is_aarch64_feature_detected!("sha3")); + println!("f32mm: {:?}", is_aarch64_feature_detected!("f32mm")); + println!("f64mm: {:?}", is_aarch64_feature_detected!("f64mm")); } #[test] diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 473c5f3e0faab..c545f521a6e1a 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1073,6 +1073,8 @@ impl Builder<'_> { // rustc creates absolute paths (in part bc of the `rust-src` unremap // and for working directory) so let's remap the build directory as well. format!("{}={map_to}", self.build.src.display()), + // remap OUT_DIR so they don't leak into artifacts. + format!("{}={map_to}/out", self.build.out.display()), ] .join("\t"); cargo.env("RUSTC_DEBUGINFO_MAP", map); @@ -1093,6 +1095,8 @@ impl Builder<'_> { // rustc creates absolute paths (in part bc of the `rust-src` unremap // and for working directory) so let's remap the build directory as well. format!("{}={map_to}", self.build.src.display()), + // remap OUT_DIR so they don't leak into artifacts. + format!("{}={map_to}/out", self.build.out.display()), ] .join("\t"); cargo.env("RUSTC_DEBUGINFO_MAP", map); diff --git a/src/ci/scripts/free-disk-space-linux.sh b/src/ci/scripts/free-disk-space-linux.sh index ac06f3fad88c0..590e594e6aef4 100755 --- a/src/ci/scripts/free-disk-space-linux.sh +++ b/src/ci/scripts/free-disk-space-linux.sh @@ -96,15 +96,11 @@ removeUnusedFilesAndDirs() { ) if isGitHubRunner; then + # Paths common to all runners (both x86 and ARM) to_remove+=( "/usr/local/aws-sam-cli" "/usr/local/doc/cmake" - "/usr/local/julia"* - "/usr/local/lib/android" - "/usr/local/share/chromedriver-"* - "/usr/local/share/chromium" "/usr/local/share/cmake-"* - "/usr/local/share/edge_driver" "/usr/local/share/emacs" "/usr/local/share/gecko_driver" "/usr/local/share/icons" @@ -112,16 +108,13 @@ removeUnusedFilesAndDirs() { "/usr/local/share/vcpkg" "/usr/local/share/vim" "/usr/share/apache-maven-"* - "/usr/share/gradle-"* "/usr/share/kotlinc" - "/usr/share/miniconda" "/usr/share/php" "/usr/share/ri" "/usr/share/swift" # binaries "/usr/local/bin/azcopy" - "/usr/local/bin/bicep" "/usr/local/bin/ccmake" "/usr/local/bin/cmake-"* "/usr/local/bin/cmake" @@ -135,16 +128,53 @@ removeUnusedFilesAndDirs() { "/usr/local/bin/phpunit" "/usr/local/bin/pulumi-"* "/usr/local/bin/pulumi" - "/usr/local/bin/stack" - - # Haskell runtime - "/usr/local/.ghcup" # Azure "/opt/az" "/usr/share/az_"* + + # Microsoft Edge and powershell + "/opt/microsoft" + + "/opt/pipx" + "/opt/pipx_bin" + ) + + # Paths only present in x86 runners + local github_runner_x86_paths=( + "/usr/local/julia"* + "/usr/local/lib/android" + "/usr/local/share/chromedriver-"* + "/usr/local/share/chromium" + "/usr/local/share/edge_driver" + "/usr/share/gradle-"* + "/usr/share/miniconda" + + # binaries + "/usr/local/bin/bicep" + "/usr/local/bin/stack" + + # Haskell runtime + "/usr/local/.ghcup" ) + if isX86; then + to_remove+=("${github_runner_x86_paths[@]}") + else + # warn if x86-only paths are present in other runners + local existing_github_runner_x86_paths=() + local x86_path + for x86_path in "${github_runner_x86_paths[@]}"; do + if [ -e "$x86_path" ]; then + existing_github_runner_x86_paths+=("$x86_path") + fi + done + + if [ "${#existing_github_runner_x86_paths[@]}" -ne 0 ]; then + echo "::warning::You can remove the following paths to save space: ${existing_github_runner_x86_paths[*]}" + fi + fi + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then # Environment variable set by GitHub Actions to_remove+=( @@ -201,10 +231,22 @@ cleanPackages() { '^dotnet-.*' '^llvm-.*' '^mongodb-.*' + '^temurin-.*-jdk' + 'buildah' 'firefox' + 'google-cloud-cli' + 'google-cloud-sdk' + 'kubectl' 'libgl1-mesa-dri' 'mono-devel' 'php.*' + 'podman' + 'skopeo' + ) + local x86_only_packages=( + 'google-chrome-stable' + 'microsoft-edge-stable' + 'powershell' ) if isGitHubRunner; then @@ -213,12 +255,20 @@ cleanPackages() { ) if isX86; then - packages+=( - 'google-chrome-stable' - 'google-cloud-cli' - 'google-cloud-sdk' - 'powershell' - ) + packages+=("${x86_only_packages[@]}") + else + # warn if x86-only packages are installed on other runners + local installed_x86_only_packages=() + local package + for package in "${x86_only_packages[@]}"; do + if dpkg-query -W -f='${binary:Package}\n' "$package" >/dev/null 2>&1; then + installed_x86_only_packages+=("$package") + fi + done + + if [ "${#installed_x86_only_packages[@]}" -ne 0 ]; then + echo "::warning::You can remove the following packages to save space: ${installed_x86_only_packages[*]}" + fi fi else packages+=( @@ -235,11 +285,19 @@ cleanPackages() { || echo "::warning::The command [sudo apt-get clean] failed" } -# Remove Docker images. -# Ubuntu 22 runners have docker images already installed. -# They aren't present in ubuntu 24 runners. +# Remove preinstalled Docker images. cleanDocker() { + local images + images=$(sudo docker image ls -q) + + if [ -z "$images" ]; then + echo "=> No docker images to remove." + return + fi + echo "=> Removing the following docker images:" + # Use "docker image ls" without "-q" to get the full table output which contains + # also the image names and sizes. sudo docker image ls echo "=> Removing docker images..." sudo docker image prune --all --force || true @@ -253,7 +311,8 @@ cleanSwap() { } sufficientSpaceEarlyExit() { - local available_space_kb=$(df -k . --output=avail | tail -n 1) + local available_space_kb + available_space_kb=$(df -k . --output=avail | tail -n 1) if [ "$available_space_kb" -ge "$space_target_kb" ]; then echo "Sufficient disk space available (${available_space_kb}KB >= ${space_target_kb}KB). Skipping cleanup." @@ -267,7 +326,8 @@ sufficientSpaceEarlyExit() { checkAlternative() { local gha_alt_disk="/mnt" - local available_space_kb=$(df -k "$gha_alt_disk" --output=avail | tail -n 1) + local available_space_kb + available_space_kb=$(df -k "$gha_alt_disk" --output=avail | tail -n 1) # mount options that trade durability for performance # ignore-tidy-linelength @@ -276,7 +336,8 @@ checkAlternative() { # GHA has a 2nd disk mounted at /mnt that is almost empty. # Check if it's a valid mountpoint and it has enough available space. if mountpoint "$gha_alt_disk" && [ "$available_space_kb" -ge "$space_target_kb" ]; then - local blkdev=$(df -k "$gha_alt_disk" --output=source | tail -n 1) + local blkdev + blkdev=$(df -k "$gha_alt_disk" --output=source | tail -n 1) echo "Sufficient space available on $blkdev mounted at $gha_alt_disk" # see cleanSwap(), swapfile may be mounted under /mnt sudo swapoff -a || true diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a4e4126c1b9b2..28021d3449437 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3260,14 +3260,22 @@ fn clean_maybe_renamed_foreign_item<'tcx>( hir::ForeignItemKind::Type => ForeignTypeItem, }; - generate_item_with_correct_attrs( + let mut clean_item = generate_item_with_correct_attrs( cx, kind, item.owner_id.def_id.to_def_id(), item.ident.name, import_id.as_slice(), renamed, - ) + ); + // We also need to take into account the `extern` block (doc_)cfg attributes. + let mut attrs = Attributes::from_hir(inline::load_attrs( + cx.tcx, + cx.tcx.hir_owner_parent(item.owner_id).owner.to_def_id(), + )); + attrs.merge_with(std::mem::take(&mut clean_item.inner.attrs)); + clean_item.inner.attrs = attrs; + clean_item }) } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index e81c6eae1996f..b2e38e491c704 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1093,6 +1093,12 @@ impl Attributes { } aliases.into_iter().collect::>().into() } + + pub(crate) fn merge_with(&mut self, other: Self) { + let Self { doc_strings, other_attrs } = other; + self.doc_strings.extend(doc_strings); + self.other_attrs.extend(other_attrs); + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index f51cd96486b4f..6edfd98c5e059 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -511,6 +511,8 @@ to Miri failing to detect cases of undefined behavior in a program. of Rust will be stricter than Tree Borrows. In other words, if you use Tree Borrows, even if your code is accepted today, it might be declared UB in the future. This is much less likely with Stacked Borrows. +* `-Zmiri-tree-borrows-implicit-writes` enables implicit writes for all `&mut` function arguments. + This makes Tree Borrows less permissive. * `-Zmiri-tree-borrows-no-precise-interior-mut` makes Tree Borrows track interior mutable data on the level of references instead of on the byte-level as is done by default. Therefore, with this flag, Tree diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 273ac90068b01..b1a3d470d25ae 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -515,6 +515,7 @@ fn main() -> ExitCode { miri_config.borrow_tracker = Some(BorrowTrackerMethod::TreeBorrows(TreeBorrowsParams { precise_interior_mut: true, + implicit_writes: false, })); } else if arg == "-Zmiri-tree-borrows-no-precise-interior-mut" { match &mut miri_config.borrow_tracker { @@ -526,6 +527,16 @@ fn main() -> ExitCode { "`-Zmiri-tree-borrows` is required before `-Zmiri-tree-borrows-no-precise-interior-mut`" ), }; + } else if arg == "-Zmiri-tree-borrows-implicit-writes" { + match &mut miri_config.borrow_tracker { + Some(BorrowTrackerMethod::TreeBorrows(params)) => { + params.implicit_writes = true; + } + _ => + fatal_error!( + "`-Zmiri-tree-borrows` is required before `-Zmiri-tree-borrows-implicit-writes`" + ), + }; } else if arg == "-Zmiri-disable-data-race-detector" { miri_config.data_race_detector = false; miri_config.weak_memory_emulation = false; diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 9aa0ce48e09a0..d53de57662e8d 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -224,6 +224,8 @@ pub enum BorrowTrackerMethod { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TreeBorrowsParams { pub precise_interior_mut: bool, + /// Controls whether `&mut` function arguments are immediately activated with an implicit write. + pub implicit_writes: bool, } impl BorrowTrackerMethod { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index 093dada405af8..16e89ee460de0 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -15,7 +15,7 @@ use crate::*; #[derive(Clone, Copy, Debug)] pub enum AccessCause { Explicit(AccessKind), - Reborrow, + Reborrow(AccessKind), Dealloc, FnExit(AccessKind), } @@ -24,7 +24,7 @@ impl fmt::Display for AccessCause { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Explicit(kind) => write!(f, "{kind}"), - Self::Reborrow => write!(f, "reborrow"), + Self::Reborrow(_) => write!(f, "reborrow"), Self::Dealloc => write!(f, "deallocation"), // This is dead code, since the protector release access itself can never // cause UB (while the protector is active, if some other access invalidates @@ -40,7 +40,7 @@ impl AccessCause { let rel = if is_foreign { "foreign" } else { "child" }; match self { Self::Explicit(kind) => format!("{rel} {kind}"), - Self::Reborrow => format!("reborrow (acting as a {rel} read access)"), + Self::Reborrow(kind) => format!("reborrow (acting as a {rel} {kind})"), Self::Dealloc => format!("deallocation (acting as a {rel} write access)"), Self::FnExit(kind) => format!("protector release (acting as a {rel} {kind})"), } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index b191359501d18..2b4f3cada96d5 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty}; use self::foreign_access_skipping::IdempotentForeignAccess; use self::tree::LocationState; use crate::borrow_tracker::{AccessKind, GlobalState, GlobalStateInner, ProtectorKind}; -use crate::concurrency::data_race::NaReadType; +use crate::concurrency::data_race::{NaReadType, NaWriteType}; use crate::*; pub mod diagnostics; @@ -109,13 +109,8 @@ impl<'tcx> Tree { pub struct NewPermission { /// Permission for the frozen part of the range. freeze_perm: Permission, - /// Whether a read access should be performed on the frozen part on a retag. - freeze_access: bool, /// Permission for the non-frozen part of the range. nonfreeze_perm: Permission, - /// Whether a read access should be performed on the non-frozen - /// part on a retag. - nonfreeze_access: bool, /// Permission for memory outside the range. outside_perm: Permission, /// Whether this pointer is part of the arguments of a function call. @@ -138,36 +133,78 @@ impl<'tcx> NewPermission { let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); let is_protected = retag_kind == RetagKind::FnEntry; + // Check if the implicit writes check has been enabled for this function using the `-Zmiri-tree-borrows-implicit-writes` flag + let implicit_writes = cx + .machine + .borrow_tracker + .as_ref() + .unwrap() + .borrow() + .borrow_tracker_method + .get_tree_borrows_params() + .implicit_writes; + if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) { // Mutable reference / Box to pinning type: retagging is a NOP. // FIXME: with `UnsafePinned`, this should do proper per-byte tracking. return None; } - let freeze_perm = match ref_mutability { - // Shared references are frozen. - Some(Mutability::Not) => Permission::new_frozen(), - // Mutable references and Boxes are reserved. - _ => Permission::new_reserved_frz(), - }; - let nonfreeze_perm = match ref_mutability { - // Shared references are "transparent". - Some(Mutability::Not) => Permission::new_cell(), - // *Protected* mutable references and boxes are reserved without regarding for interior mutability. - _ if is_protected => Permission::new_reserved_frz(), - // Unprotected mutable references and boxes start in `ReservedIm`. - _ => Permission::new_reserved_im(), + enum Part { + InsideFrozen, + InsideUnsafeCell, + Outside, + } + use Part::*; + + let perm = |part: Part| { + // Whether we should consider this byte to be frozen. + // Outside bytes are frozen only if the entire type is frozen. + let frozen = match part { + InsideFrozen => true, + InsideUnsafeCell => false, + Outside => ty_is_freeze, + }; + match ref_mutability { + // Shared references + Some(Mutability::Not) => + if frozen { + Permission::new_frozen() + } else { + Permission::new_cell() + }, + // Mutable references + Some(Mutability::Mut) => { + if is_protected && implicit_writes && !matches!(part, Outside) { + // We cannot use `Unique` for the outside part. + Permission::new_unique() + } else if is_protected || frozen { + // We also use this for protected `&mut UnsafeCell` as otherwise adding + // `noalias` would not be sound. + Permission::new_reserved_frz() + } else { + Permission::new_reserved_im() + } + } + // Boxes + None => + if is_protected && implicit_writes && !matches!(part, Outside) { + // Boxes are treated the same as mutable references. + Permission::new_unique() + } else if is_protected || frozen { + // We also use this for protected `Box` as otherwise adding + // `noalias` would not be sound. + Permission::new_reserved_frz() + } else { + Permission::new_reserved_im() + }, + } }; - // Everything except for `Cell` gets an initial access. - let initial_access = |perm: &Permission| !perm.is_cell(); - Some(NewPermission { - freeze_perm, - freeze_access: initial_access(&freeze_perm), - nonfreeze_perm, - nonfreeze_access: initial_access(&nonfreeze_perm), - outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm }, + freeze_perm: perm(InsideFrozen), + nonfreeze_perm: perm(InsideUnsafeCell), + outside_perm: perm(Outside), protector: is_protected.then_some(if ref_mutability.is_some() { // Strong protector for references ProtectorKind::StrongProtector @@ -288,13 +325,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Compute initial "inside" permissions. let loc_state = |frozen: bool| -> LocationState { - let (perm, access) = if frozen { - (new_perm.freeze_perm, new_perm.freeze_access) - } else { - (new_perm.nonfreeze_perm, new_perm.nonfreeze_access) - }; + let perm = if frozen { new_perm.freeze_perm } else { new_perm.nonfreeze_perm }; let sifa = perm.strongest_idempotent_foreign_access(protected); - if access { + + if perm.associated_access().is_some() { LocationState::new_accessed(perm, sifa) } else { LocationState::new_non_accessed(perm, sifa) @@ -303,11 +337,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let inside_perms = if !precise_interior_mut { // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`. let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); - let state = loc_state(ty_is_freeze); - DedupRangeMap::new(ptr_size, state) + DedupRangeMap::new(ptr_size, loc_state(ty_is_freeze)) } else { // The initial state will be overwritten by the visitor below. - let mut perms_map: DedupRangeMap = DedupRangeMap::new( + let mut perms_map = DedupRangeMap::new( ptr_size, LocationState::new_accessed( Permission::new_disabled(), @@ -327,9 +360,18 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let alloc_extra = this.get_alloc_extra(alloc_id)?; let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut(); - for (perm_range, perm) in inside_perms.iter_all() { - if perm.accessed() { - // Some reborrows incur a read access to the parent. + for (perm_range, loc_state) in inside_perms.iter_all() { + if let Some(access) = loc_state.permission().associated_access() { + // Some reborrows incur a read/write access to the parent. + // As a write also implies a read, a single write is performed instead of a read and a write. + + // writing to an immutable allocation (static variables) is UB, check this here + if access == AccessKind::Write + && this.get_alloc_mutability(alloc_id).unwrap().is_not() + { + throw_ub!(WriteToReadOnly(alloc_id)) + } + // Adjust range to be relative to allocation start (rather than to `place`). let range_in_alloc = AllocRange { start: Size::from_bytes(perm_range.start) + base_offset, @@ -339,8 +381,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { tree_borrows.perform_access( parent_prov, range_in_alloc, - AccessKind::Read, - diagnostics::AccessCause::Reborrow, + access, + diagnostics::AccessCause::Reborrow(access), this.machine.borrow_tracker.as_ref().unwrap(), alloc_id, this.machine.current_user_relevant_span(), @@ -349,17 +391,29 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Also inform the data race model (but only if any bytes are actually affected). if range_in_alloc.size.bytes() > 0 { if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() { - data_race.read_non_atomic( - alloc_id, - range_in_alloc, - NaReadType::Retag, - Some(place.layout.ty), - &this.machine, - )? + match access { + AccessKind::Read => + data_race.read_non_atomic( + alloc_id, + range_in_alloc, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?, + AccessKind::Write => + data_race.write_non_atomic( + alloc_id, + range_in_alloc, + NaWriteType::Retag, + Some(place.layout.ty), + &this.machine, + )?, + }; } } } } + // Record the parent-child pair in the tree. tree_borrows.new_child( base_offset, @@ -370,7 +424,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { protected, this.machine.current_user_relevant_span(), )?; - drop(tree_borrows); interp_ok(Some(new_prov)) } @@ -550,9 +603,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // argument doesn't matter // (`ty_is_freeze || true` in `new_reserved` will always be `true`). freeze_perm: Permission::new_reserved_frz(), - freeze_access: true, nonfreeze_perm: Permission::new_reserved_frz(), - nonfreeze_access: true, outside_perm: Permission::new_reserved_frz(), protector: Some(ProtectorKind::StrongProtector), }; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index b62c5f242c374..aa15b9e1b1188 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -91,7 +91,7 @@ impl PartialOrd for PermissionPriv { impl PermissionPriv { /// Check if `self` can be the initial state of a pointer. fn is_initial(&self) -> bool { - matches!(self, ReservedFrz { conflicted: false } | Frozen | ReservedIM | Cell) + matches!(self, ReservedFrz { conflicted: false } | Frozen | ReservedIM | Cell | Unique) } /// Reject `ReservedIM` that cannot exist in the presence of a protector. @@ -265,14 +265,17 @@ impl Permission { self.inner == Cell } - /// Default initial permission of the root of a new tree at inbounds positions. - /// Must *only* be used for the root, this is not in general an "initial" permission! + /// Check if `self` is a Permission of type `Unique` + pub fn is_unique(&self) -> bool { + self.inner == Unique + } + + /// Create a new Permission of type `Unique` pub fn new_unique() -> Self { Self { inner: Unique } } - /// Default initial permission of a reborrowed mutable reference that is either - /// protected or not interior mutable. + /// Create a new Permission of type `ReservedFrz` with conflictedReserved set to false pub fn new_reserved_frz() -> Self { Self { inner: ReservedFrz { conflicted: false } } } @@ -304,8 +307,8 @@ impl Permission { self.inner.compatible_with_protector() } - /// What kind of access to perform before releasing the protector. - pub fn protector_end_access(&self) -> Option { + /// What kind of access to perform before releasing the protector or on a reborrow. + pub fn associated_access(&self) -> Option { match self.inner { // Do not do perform access if it is a `Cell`, as this // can cause data races when using thread-safe data types. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 54a0e0cefe743..296de803d5828 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -63,6 +63,7 @@ impl LocationState { /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs` pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self { assert!(permission.is_initial() || permission.is_disabled()); + assert!(!permission.is_unique()); Self { permission, accessed: false, idempotent_foreign_access: sifa } } @@ -73,6 +74,12 @@ impl LocationState { Self { permission, accessed: true, idempotent_foreign_access: sifa } } + /// Checks whether the current location state is ever reachable in a real execution. + pub fn possible(&self) -> bool { + // `Unique` can only be reached on actually accessed locations. + self.accessed || !self.permission.is_unique() + } + /// Check if the location has been accessed, i.e. if it has /// ever been accessed through a child pointer. pub fn accessed(&self) -> bool { @@ -150,6 +157,7 @@ impl LocationState { if protected && self.accessed && transition.produces_disabled() { return Err(TransitionError::ProtectedDisabled(old_perm)); } + debug_assert!(self.possible()); Ok(transition) } @@ -397,6 +405,7 @@ impl<'tcx> Tree { ProvenanceExtra::Wildcard => None, }; assert!(outside_perm.is_initial()); + assert!(!outside_perm.is_unique()); let default_strongest_idempotent = outside_perm.strongest_idempotent_foreign_access(protected); @@ -690,7 +699,7 @@ impl<'tcx> Tree { for (loc_range, loc) in self.locations.iter_mut_all() { // Only visit accessed permissions if let Some(p) = loc.perms.get(source_idx) - && let Some(access_kind) = p.permission.protector_end_access() + && let Some(access_kind) = p.permission.associated_access() && p.accessed { let diagnostics = DiagnosticInfo { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 52fe1c08a3092..033ef5d09a99f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -9,13 +9,17 @@ use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition}; impl Exhaustive for LocationState { fn exhaustive() -> Box> { // We keep `latest_foreign_access` at `None` as that's just a cache. - Box::new(<(Permission, bool)>::exhaustive().map(|(permission, accessed)| { - Self { - permission, - accessed, - idempotent_foreign_access: IdempotentForeignAccess::default(), - } - })) + Box::new( + <(Permission, bool)>::exhaustive() + .map(|(permission, accessed)| { + Self { + permission, + accessed, + idempotent_foreign_access: IdempotentForeignAccess::default(), + } + }) + .filter(|x| x.possible()), + ) } } @@ -438,17 +442,19 @@ mod spurious_read { /// Perform a read on the given pointer if its state is `accessed`. /// Must be called just after reborrowing a pointer, and just after /// removing a protector. - fn read_if_accessed(self, ptr: PtrSelector) -> Result { + fn retag_dependent_access(self, ptr: PtrSelector) -> Result { let accessed = match ptr { - PtrSelector::X => self.x.state.accessed, - PtrSelector::Y => self.y.state.accessed, + PtrSelector::X => + self.x.state.permission.associated_access().filter(|_| self.x.state.accessed), + PtrSelector::Y => + self.y.state.permission.associated_access().filter(|_| self.y.state.accessed), PtrSelector::Other => panic!( "the `accessed` status of `PtrSelector::Other` is unknown, do not pass it to `read_if_accessed`" ), }; - if accessed { - self.perform_test_access(&TestAccess { ptr, kind: AccessKind::Read }) + if let Some(kind) = accessed { + self.perform_test_access(&TestAccess { ptr, kind }) } else { Ok(self) } @@ -457,13 +463,13 @@ mod spurious_read { /// Remove the protector of `x`, including the implicit read on function exit. fn end_protector_x(self) -> Result { let x = self.x.end_protector(); - Self { x, ..self }.read_if_accessed(PtrSelector::X) + Self { x, ..self }.retag_dependent_access(PtrSelector::X) } /// Remove the protector of `y`, including the implicit read on function exit. fn end_protector_y(self) -> Result { let y = self.y.end_protector(); - Self { y, ..self }.read_if_accessed(PtrSelector::Y) + Self { y, ..self }.retag_dependent_access(PtrSelector::Y) } fn retag_y(self, new_y: LocStateProt) -> Result { @@ -473,7 +479,7 @@ mod spurious_read { } // `xy_rel` changes to "mutually foreign" now: `y` can no longer be a parent of `x`. Self { y: new_y, xy_rel: RelPosXY::MutuallyForeign, ..self } - .read_if_accessed(PtrSelector::Y) + .retag_dependent_access(PtrSelector::Y) } fn perform_test_event(self, evt: &TestEvent) -> Result { @@ -696,7 +702,7 @@ mod spurious_read { fn initial_state(&self) -> Result { let (x, y) = self.retag_permissions(); let state = LocStateProtPair { xy_rel: self.xy_rel, x, y }; - state.read_if_accessed(PtrSelector::X) + state.retag_dependent_access(PtrSelector::X) } } diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 336abff6c52d6..76f735d457cf5 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -1243,21 +1243,21 @@ impl VClockAlloc { /// operation. The `ty` parameter is used for diagnostics, letting /// the user know which type was written. pub fn write_non_atomic<'tcx>( - &mut self, + &self, alloc_id: AllocId, access_range: AllocRange, write_type: NaWriteType, ty: Option>, - machine: &mut MiriMachine<'_>, + machine: &MiriMachine<'_>, ) -> InterpResult<'tcx> { let current_span = machine.current_user_relevant_span(); - let global = machine.data_race.as_vclocks_mut().unwrap(); + let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return interp_ok(()); } let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); for (mem_clocks_range, mem_clocks) in - self.alloc_ranges.get_mut().iter_mut(access_range.start, access_range.size) + self.alloc_ranges.borrow_mut().iter_mut(access_range.start, access_range.size) { if let Err(DataRace) = mem_clocks.write_race_detect(&mut thread_clocks, index, write_type, current_span) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 2a6520ffc2ba5..512ea6ab1a2ff 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -407,7 +407,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let sig = this.tcx.mk_fn_sig( args.iter().map(|a| a.layout.ty), dest.layout.ty, - FnSigKind::default().set_abi(caller_abi).set_safe(true), + FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe), ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index ddfde35f47c4d..68b13a6ed58a0 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -275,7 +275,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fn_sig_binder = Binder::dummy(FnSig { inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), // Safety does not matter for the ABI. - fn_sig_kind: FnSigKind::default().set_abi(shim_sig.abi).set_safe(true), + fn_sig_kind: FnSigKind::default() + .set_abi(shim_sig.abi) + .set_safety(rustc_hir::Safety::Safe), }); let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index ba12985e86fcc..c384b24abb163 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -350,6 +350,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.fstat(fd, buf)?; this.write_scalar(result, dest)?; } + "lstat" => { + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let result = this.lstat(path, buf)?; + this.write_scalar(result, dest)?; + } + "stat" => { + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let result = this.stat(path, buf)?; + this.write_scalar(result, dest)?; + } "rename" => { // FIXME: This does not have a direct test (#3179). let [oldpath, newpath] = this.check_shim_sig( diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index de08f4c6afe46..332bd26eff899 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -136,16 +136,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // File related shims - // For those, we both intercept `func` and `call@FBSD_1.0` symbols cases - // since freebsd 12 the former form can be expected. - "stat" | "stat@FBSD_1.0" => { - // FIXME: This does not have a direct test (#3179). + "stat@FBSD_1.0" => { let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } - "lstat" | "lstat@FBSD_1.0" => { - // FIXME: This does not have a direct test (#3179). + "lstat@FBSD_1.0" => { let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0988edaef2b0f..b1d39513065ce 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -243,12 +243,12 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields_named( &[ - ("st_dev", metadata.dev.into()), + ("st_dev", metadata.dev.unwrap_or(0).into()), ("st_mode", mode.into()), - ("st_nlink", 0), - ("st_ino", 0), - ("st_uid", metadata.uid.into()), - ("st_gid", metadata.gid.into()), + ("st_nlink", metadata.nlink.unwrap_or(0).into()), + ("st_ino", metadata.ino.unwrap_or(0).into()), + ("st_uid", metadata.uid.unwrap_or(0).into()), + ("st_gid", metadata.gid.unwrap_or(0).into()), ("st_rdev", 0), ("st_atime", access_sec.into()), ("st_atime_nsec", access_nsec.into()), @@ -257,8 +257,8 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { ("st_ctime", 0), ("st_ctime_nsec", 0), ("st_size", metadata.size.into()), - ("st_blocks", 0), - ("st_blksize", 0), + ("st_blocks", metadata.blocks.unwrap_or(0).into()), + ("st_blksize", metadata.blksize.unwrap_or(0).into()), ], &buf, )?; @@ -586,9 +586,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !matches!( &this.tcx.sess.target.os, - Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Android + Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Android | Os::Linux ) { - panic!("`macos_fbsd_solaris_stat` should not be called on {}", this.tcx.sess.target.os); + panic!("`stat` should not be called on {}", this.tcx.sess.target.os); } let path_scalar = this.read_pointer(path_op)?; @@ -615,12 +615,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !matches!( &this.tcx.sess.target.os, - Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Android + Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Android | Os::Linux ) { - panic!( - "`macos_fbsd_solaris_lstat` should not be called on {}", - this.tcx.sess.target.os - ); + panic!("`lstat` should not be called on {}", this.tcx.sess.target.os); } let path_scalar = this.read_pointer(path_op)?; @@ -730,12 +727,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(ecode); } - // the `_mask_op` parameter specifies the file information that the caller requested. - // However `statx` is allowed to return information that was not requested or to not - // return information that was requested. This `mask` represents the information we can - // actually provide for any target. - let mut mask = this.eval_libc_u32("STATX_TYPE") | this.eval_libc_u32("STATX_SIZE"); - // If the `AT_SYMLINK_NOFOLLOW` flag is set, we query the file's metadata without following // symbolic links. let follow_symlink = flags & this.eval_libc_i32("AT_SYMLINK_NOFOLLOW") == 0; @@ -752,6 +743,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(err) => return this.set_last_error_and_return_i32(err), }; + // The `_mask_op` parameter specifies the file information that the caller requested. + // However, `statx` is allowed to return information that was not requested or to not + // return information that was requested. This `mask` represents the information we can + // actually provide for any target. + let mut mask = this.eval_libc_u32("STATX_TYPE") | this.eval_libc_u32("STATX_SIZE"); + + // Check which pieces of metadata we acquired, and set the appropriate flags in the mask. + if metadata.ino.is_some() { + mask |= this.eval_libc_u32("STATX_INO"); + } + if metadata.nlink.is_some() { + mask |= this.eval_libc_u32("STATX_NLINK"); + } + if metadata.uid.is_some() { + mask |= this.eval_libc_u32("STATX_UID"); + } + if metadata.gid.is_some() { + mask |= this.eval_libc_u32("STATX_GID"); + } + if metadata.blocks.is_some() { + mask |= this.eval_libc_u32("STATX_BLOCKS"); + } + // `statx.stx_mode` is `__u16`. `libc::S_IF*` are of type `mode_t`, which varies in // width across targets (`u16` on macOS, `u32` on Linux). Read using `mode_t`'s size. let mode_t_size = this.libc_ty_layout("mode_t").size; @@ -791,15 +805,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields_named( &[ ("stx_mask", mask.into()), - ("stx_blksize", 0), + ("stx_blksize", metadata.blksize.unwrap_or(0).into()), ("stx_attributes", 0), - ("stx_nlink", 0), - ("stx_uid", 0), - ("stx_gid", 0), + ("stx_nlink", metadata.nlink.unwrap_or(0).into()), + ("stx_uid", metadata.uid.unwrap_or(0).into()), + ("stx_gid", metadata.gid.unwrap_or(0).into()), ("stx_mode", mode.into()), - ("stx_ino", 0), + ("stx_ino", metadata.ino.unwrap_or(0).into()), ("stx_size", metadata.size.into()), - ("stx_blocks", 0), + ("stx_blocks", metadata.blocks.unwrap_or(0).into()), ("stx_attributes_mask", 0), ("stx_rdev_major", 0), ("stx_rdev_minor", 0), @@ -1664,15 +1678,24 @@ fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str { /// Stores a file's metadata in order to avoid code duplication in the different metadata related /// shims. +/// +/// Some fields are host/platform-specific. `None` means that Miri does not have a real value for +/// this field, for example because the metadata is synthetic or because the host platform does not +/// expose it. `statx` must only advertise the corresponding `STATX_*` bit when the field is `Some`; +/// legacy `stat` writes zero for `None` to preserve the old fallback behavior. struct FileMetadata { mode: Scalar, size: u64, created: Option<(u64, u32)>, accessed: Option<(u64, u32)>, modified: Option<(u64, u32)>, - dev: u64, - uid: u32, - gid: u32, + dev: Option, + ino: Option, + nlink: Option, + uid: Option, + gid: Option, + blksize: Option, + blocks: Option, } impl FileMetadata { @@ -1711,9 +1734,13 @@ impl FileMetadata { created: None, accessed: None, modified: None, - dev: 0, - uid: 0, - gid: 0, + dev: None, + uid: None, + gid: None, + blksize: None, + blocks: None, + ino: None, + nlink: None, })) } @@ -1743,16 +1770,42 @@ impl FileMetadata { unix => { use std::os::unix::fs::MetadataExt; let dev = metadata.dev(); + let ino = metadata.ino(); + let nlink = metadata.nlink(); let uid = metadata.uid(); let gid = metadata.gid(); + let blksize = metadata.blksize(); + let blocks = metadata.blocks(); + + interp_ok(Ok(FileMetadata { + mode, + size, + created, + accessed, + modified, + dev: Some(dev), + ino: Some(ino), + nlink: Some(nlink), + uid: Some(uid), + gid: Some(gid), + blksize: Some(blksize), + blocks: Some(blocks), + })) } - _ => { - let dev = 0; - let uid = 0; - let gid = 0; - } + _ => interp_ok(Ok(FileMetadata { + mode, + size, + created, + accessed, + modified, + dev: None, + ino: None, + nlink: None, + uid: None, + gid: None, + blksize: None, + blocks: None, + })), } - - interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified, dev, uid, gid })) } } diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index b92732de73ca0..f679f27aed236 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -124,7 +124,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "statx" => { - // FIXME: This does not have a direct test (#3179). let [dirfd, pathname, flags, mask, statxbuf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; 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 9ca487eac9ae9..ac56ffef8f8f6 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -46,14 +46,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.close(result)?; this.write_scalar(result, dest)?; } - "stat" | "stat$INODE64" => { - // FIXME: This does not have a direct test (#3179). + "stat$INODE64" => { let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } - "lstat" | "lstat$INODE64" => { - // FIXME: This does not have a direct test (#3179). + "lstat$INODE64" => { let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index c553cd1f70e8b..e2bdd459c1003 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -316,10 +316,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { flags ); } - if protocol != 0 { + if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") { throw_unsup_format!( "socket: socket protocol {protocol} is unsupported, \ - only 0 is allowed" + only IPPROTO_TCP and 0 are allowed" ); } diff --git a/src/tools/miri/src/shims/x86/avx512.rs b/src/tools/miri/src/shims/x86/avx512.rs index fe4adf971c0d7..ebfd3c830347d 100644 --- a/src/tools/miri/src/shims/x86/avx512.rs +++ b/src/tools/miri/src/shims/x86/avx512.rs @@ -3,7 +3,9 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use super::{packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, pmaddwd, psadbw, pshufb}; +use super::{ + packssdw, packsswb, packusdw, packuswb, permute, permute2, pmaddbw, pmaddwd, psadbw, pshufb, +}; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -111,6 +113,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { permute(this, left, right, dest)?; } + // Used to implement the _mm512_permutex2var_epi64 intrinsic. + "vpermi2var.q.512" => { + let [left, indices, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + permute2(this, left, indices, right, dest)?; + } // Used to implement the _mm512_shuffle_epi8 intrinsic. "pshuf.b.512" => { let [left, right] = diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index e6e7f4b6f09f9..a1e1d5d2740c1 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -1107,6 +1107,54 @@ fn permute<'tcx>( interp_ok(()) } +/// Shuffle elements from *two* source registers (`left` and `right`) using +/// the corresponding index in `indices`, and store the results in `dest`. +/// +/// For a vector with `N` lanes, the low `log2(N)` bits of each index select a +/// lane within a source vector. Bit `log2(N)` selects the source vector (`0` => +/// `left`, `1` => `right`), and all higher bits are ignored. +/// Equivalently, lane `i` of the result is copied from +/// `src[indices[i] & (N - 1)]` where +/// `src = if indices[i] & N == 0 { left } else { right }`. +/// +/// +fn permute2<'tcx>( + ecx: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + indices: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = ecx.project_to_simd(left)?; + let (indices, indices_len) = ecx.project_to_simd(indices)?; + let (right, right_len) = ecx.project_to_simd(right)?; + let (dest, dest_len) = ecx.project_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, indices_len); + assert_eq!(dest_len, right_len); + + // Use the low bits to select a lane within either input vector, and the next bit to + // choose between the two vectors. + assert!(dest_len.is_power_of_two()); + let lane_mask = u128::from(dest_len).strict_sub(1); + let vector_select_bit = u128::from(dest_len); + + for i in 0..dest_len { + let dest = ecx.project_index(&dest, i)?; + let index_place = ecx.project_index(&indices, i)?; + let index = ecx.read_scalar(&index_place)?.to_uint(index_place.layout.size)?; + // `lane_mask` is at most `dest_len - 1` which fits in a `u64`, so this cannot fail. + let lane = u64::try_from(index & lane_mask).unwrap(); + let src = if index & vector_select_bit == 0 { &left } else { &right }; + let element = ecx.project_index(src, lane)?; + + ecx.copy_op(&element, &dest)?; + } + + interp_ok(()) +} + /// Multiplies packed 16-bit signed integer values, truncates the 32-bit /// product to the 18 most significant bits by right-shifting, and then /// divides the 18-bit value by 2 (rounding to nearest) by first adding diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs index a8494eaf0aa49..5d5b12bca0f52 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs @@ -1,9 +1,11 @@ -//@revisions: stack tree +//@revisions: stack tree tree_iwrites //@[tree]compile-flags: -Zmiri-tree-borrows +//@[tree_iwrites]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes use std::mem; fn safe(x: &mut i32, y: &mut i32) { //~[stack]^ ERROR: protect + //~[tree_iwrites]^^ ERROR: /Undefined Behavior: reborrow through .* at .* is forbidden/ *x = 1; //~[tree] ERROR: /write access through .* is forbidden/ *y = 2; } diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree_iwrites.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree_iwrites.stderr new file mode 100644 index 0000000000000..3224109d3822c --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree_iwrites.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: reborrow through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC + | +LL | fn safe(x: &mut i32, y: &mut i32) { + | ^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: this reborrow (acting as a foreign write access) would cause the protected tag (currently Unique) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC + | +LL | let xraw: *mut i32 = unsafe { mem::transmute(&mut x) }; + | ^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC + | +LL | fn safe(x: &mut i32, y: &mut i32) { + | ^ + = note: stack backtrace: + 0: safe + at tests/fail/both_borrows/aliasing_mut1.rs:LL:CC + 1: main + at tests/fail/both_borrows/aliasing_mut1.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs index c1320a25cafa4..922f6df7c6fa1 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs @@ -1,9 +1,11 @@ -//@revisions: stack tree +//@revisions: stack tree tree_iwrites //@[tree]compile-flags: -Zmiri-tree-borrows +//@[tree_iwrites]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes use std::mem; fn safe(x: &i32, y: &mut i32) { //~[stack]^ ERROR: protect + //~[tree_iwrites]^^ ERROR: /Undefined Behavior: reborrow through .* at .* is forbidden/ let _v = *x; *y = 2; //~[tree] ERROR: /write access through .* is forbidden/ } diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree_iwrites.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree_iwrites.stderr new file mode 100644 index 0000000000000..4e0cd77627c06 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree_iwrites.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: reborrow through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC + | +LL | fn safe(x: &i32, y: &mut i32) { + | ^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: this reborrow (acting as a foreign write access) would cause the protected tag (currently Frozen) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC + | +LL | let xref = &mut x; + | ^^^^^^ +help: the protected tag was created here, in the initial state Frozen + --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC + | +LL | fn safe(x: &i32, y: &mut i32) { + | ^ + = note: stack backtrace: + 0: safe + at tests/fail/both_borrows/aliasing_mut2.rs:LL:CC + 1: main + at tests/fail/both_borrows/aliasing_mut2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs index 555e4478224c8..f24ba848d70c2 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs @@ -1,9 +1,11 @@ -//@revisions: stack tree +//@revisions: stack tree tree_iwrites //@[tree]compile-flags: -Zmiri-tree-borrows +//@[tree_iwrites]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes use std::mem; fn safe(x: &mut i32, y: &i32) { //~[stack]^ ERROR: borrow stack + //~[tree_iwrites]^^ ERROR: /Undefined Behavior: reborrow through .* at .* is forbidden/ *x = 1; //~[tree] ERROR: /write access through .* is forbidden/ let _v = *y; } diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree_iwrites.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree_iwrites.stderr new file mode 100644 index 0000000000000..1c84f12baae74 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree_iwrites.stderr @@ -0,0 +1,30 @@ +error: Undefined Behavior: reborrow through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC + | +LL | fn safe(x: &mut i32, y: &i32) { + | ^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed tag was created here, in the initial state Frozen + --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC + | +LL | let xshr = &*xref; + | ^^^^^^ +help: the accessed tag later transitioned to Disabled due to a reborrow (acting as a foreign write access) at offsets [0x0..0x4] + --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC + | +LL | fn safe(x: &mut i32, y: &i32) { + | ^ + = help: this transition corresponds to a loss of read permissions + = note: stack backtrace: + 0: safe + at tests/fail/both_borrows/aliasing_mut3.rs:LL:CC + 1: main + at tests/fail/both_borrows/aliasing_mut3.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs index 22484972f4d1c..77730a23c3c71 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs @@ -1,12 +1,14 @@ -//@revisions: stack tree +//@revisions: stack tree tree_iwrites //@[tree]compile-flags: -Zmiri-tree-borrows //@[tree]error-in-other-file: /write access through .* is forbidden/ +//@[tree_iwrites]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes use std::cell::Cell; use std::mem; // Make sure &mut UnsafeCell also is exclusive fn safe(x: &i32, y: &mut Cell) { //~[stack]^ ERROR: protect + //~[tree_iwrites]^^ ERROR: /Undefined Behavior: reborrow through .* at .* is forbidden/ y.set(1); let _load = *x; } diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree_iwrites.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree_iwrites.stderr new file mode 100644 index 0000000000000..81dbd45f6540e --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree_iwrites.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: reborrow through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/aliasing_mut4.rs:LL:CC + | +LL | fn safe(x: &i32, y: &mut Cell) { + | ^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: this reborrow (acting as a foreign write access) would cause the protected tag (currently Frozen) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/both_borrows/aliasing_mut4.rs:LL:CC + | +LL | let xref = &mut x; + | ^^^^^^ +help: the protected tag was created here, in the initial state Frozen + --> tests/fail/both_borrows/aliasing_mut4.rs:LL:CC + | +LL | fn safe(x: &i32, y: &mut Cell) { + | ^ + = note: stack backtrace: + 0: safe + at tests/fail/both_borrows/aliasing_mut4.rs:LL:CC + 1: main + at tests/fail/both_borrows/aliasing_mut4.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs index 1aefa217e2d47..8031f42895102 100644 --- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs @@ -1,4 +1,6 @@ //! A version of `cell_inside_struct` that dumps the tree so that we can see what is happening. +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows #[path = "../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr similarity index 100% rename from src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr rename to src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..282dc19eea791 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr @@ -0,0 +1,25 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 4.. 8 +| Act | Act | └─┬── +| Frz |?Cel | └──── +────────────────────────────────────────────────── +error: Undefined Behavior: write access through (a) at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/cell-inside-struct.rs:LL:CC + | +LL | (*a).field1 = 88; + | ^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag (a) has state Frozen which forbids this child write access +help: the accessed tag was created here, in the initial state Cell + --> tests/fail/tree_borrows/cell-inside-struct.rs:LL:CC + | +LL | let a = &root; + | ^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs new file mode 100644 index 0000000000000..4e273a617e11a --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs @@ -0,0 +1,18 @@ +// This code no longer works using implicit writes in tree borrows. +// This code tests that. The passing version is in `pass/tree_borrows/implicit_writes/as_mut_ptr.rs`. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +fn main() { + let mut x: [u8; 3] = [1, 2, 3]; + + let ptr = std::ptr::from_mut(&mut x); + let a = unsafe { &mut *ptr }; + let b = unsafe { &mut *ptr }; + + let _c = as_mut_ptr(a); + println!("{:?}", *b); //~ ERROR: /Undefined Behavior: reborrow through .* at .* is forbidden/ +} + +pub const fn as_mut_ptr(x: &mut [u8; 3]) -> *mut u8 { + x as *mut [u8] as *mut u8 +} diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/as_mut_ptr.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/as_mut_ptr.stderr new file mode 100644 index 0000000000000..fcf4fd76a29f7 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/as_mut_ptr.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: reborrow through at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs:LL:CC + | +LL | println!("{:?}", *b); + | ^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed tag was created here, in the initial state Reserved + --> tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs:LL:CC + | +LL | let b = unsafe { &mut *ptr }; + | ^^^^^^^^^ +help: the accessed tag later transitioned to Disabled due to a reborrow (acting as a foreign write access) at offsets [0x0..0x3] + --> tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs:LL:CC + | +LL | pub const fn as_mut_ptr(x: &mut [u8; 3]) -> *mut u8 { + | ^ + = help: this transition corresponds to a loss of read and write permissions + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.rs new file mode 100644 index 0000000000000..72b8da1c84176 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes +// This test is identical to `fail/stacked_borrows/fnentry_invalidation.rs`. +// This test shows that when `-Zmiri-tree-borrows-implicit-writes` is enabled, Tree Borrows behaves more like Stacked Borrows, and the additional write in `fail/tree_borrows/fnentry_invalidation.rs` is not needed to detect / cause UB. + +fn main() { + let mut x = 0i32; + let z = &mut x as *mut i32; + x.do_bad(); + unsafe { + let _oof = *z; //~ ERROR: /read access through .* at .* is forbidden/ + } +} + +trait Bad { + fn do_bad(&mut self) { + // who knows + } +} + +impl Bad for i32 {} diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.stderr new file mode 100644 index 0000000000000..fe588b14a3290 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: read access through at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.rs:LL:CC + | +LL | let _oof = *z; + | ^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag has state Disabled which forbids this child read access +help: the accessed tag was created here, in the initial state Reserved + --> tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.rs:LL:CC + | +LL | let z = &mut x as *mut i32; + | ^^^^^^ +help: the accessed tag later transitioned to Disabled due to a reborrow (acting as a foreign write access) at offsets [0x0..0x4] + --> tests/fail/tree_borrows/implicit_writes/fnentry_invalidation.rs:LL:CC + | +LL | fn do_bad(&mut self) { + | ^^^^^^^^^ + = help: this transition corresponds to a loss of read and write permissions + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write.rs new file mode 100644 index 0000000000000..15749ee3c6f4e --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write.rs @@ -0,0 +1,16 @@ +// Tests that UB is detected for reading from a mutable reference by another pointer (as there could be a write on that mutable reference now) +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +fn main() { + let mut x = 0u8; + let ptr = &raw mut x; + let res = dereference(&mut x, ptr); + assert_eq!(*res, 0); +} + +fn dereference(x: T, y: *mut u8) -> T { + // miri inserts an implicit write here + let _ = unsafe { *y }; //~ ERROR: read access + // x = 42; // we'd like to add this write, so there must already be UB without it because there sure is with it. + x +} diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write.stderr new file mode 100644 index 0000000000000..e88e13104815e --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: read access through (root of the allocation) at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/implicit_writes/ptr_write.rs:LL:CC + | +LL | let _ = unsafe { *y }; + | ^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/tree_borrows/implicit_writes/ptr_write.rs:LL:CC + | +LL | let ptr = &raw mut x; + | ^^^^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> tests/fail/tree_borrows/implicit_writes/ptr_write.rs:LL:CC + | +LL | fn dereference(x: T, y: *mut u8) -> T { + | ^ + = note: stack backtrace: + 0: dereference + at tests/fail/tree_borrows/implicit_writes/ptr_write.rs:LL:CC + 1: main + at tests/fail/tree_borrows/implicit_writes/ptr_write.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs new file mode 100644 index 0000000000000..4945cc12ad914 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs @@ -0,0 +1,16 @@ +// same test as `ptr_write.rs` but with `Box` +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +fn main() { + let mut x: Box = Box::new(0u8); + let ptr = &raw mut *x; + let res = dereference(x, ptr); + assert_eq!(*res, 0); +} + +fn dereference(x: T, y: *mut u8) -> T { + // miri inserts an implicit write here + let _ = unsafe { *y }; //~ ERROR: read access + // x = 42; // we'd like to add this write, so there must already be UB without it because there sure is with it. + x +} diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_box.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_box.stderr new file mode 100644 index 0000000000000..9951fd56ada05 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_box.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: read access through at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs:LL:CC + | +LL | let _ = unsafe { *y }; + | ^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs:LL:CC + | +LL | let mut x: Box = Box::new(0u8); + | ^^^^^^^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs:LL:CC + | +LL | fn dereference(x: T, y: *mut u8) -> T { + | ^ + = note: stack backtrace: + 0: dereference + at tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs:LL:CC + 1: main + at tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs new file mode 100644 index 0000000000000..b03645bec48e5 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs @@ -0,0 +1,18 @@ +// same test as `ptr_write.rs` but with `UnsafeCell` +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +use std::cell::UnsafeCell; + +fn main() { + let mut x: UnsafeCell = 0u8.into(); + let ptr = x.get(); + let res = dereference(&mut x, ptr); + assert_eq!(*res.get_mut(), 0); +} + +fn dereference(x: T, y: *mut u8) -> T { + // miri inserts an implicit write here + let _ = unsafe { *y }; //~ ERROR: read access + // x = 42; // we'd like to add this write, so there must already be UB without it because there sure is with it. + x +} diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.stderr new file mode 100644 index 0000000000000..87cf0c8ba2de7 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: read access through at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs:LL:CC + | +LL | let _ = unsafe { *y }; + | ^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: this foreign read access would cause the protected tag (currently Unique) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs:LL:CC + | +LL | let ptr = x.get(); + | ^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs:LL:CC + | +LL | fn dereference(x: T, y: *mut u8) -> T { + | ^ + = note: stack backtrace: + 0: dereference + at tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs:LL:CC + 1: main + at tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/retag_is_race.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/retag_is_race.rs new file mode 100644 index 0000000000000..b6381e5686ff1 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/retag_is_race.rs @@ -0,0 +1,47 @@ +// This is mostly the same test as `tests/pass/tree_borrows/retag_no_race.rs`, but it now fails under implicit writes, meaning that there is now Undefined Behaviour. It has been changed slightly have the same interleaving on all targets. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes +// This test relies on a specific interleaving that cannot be enforced with just barriers. We must remove preemption so that the execution and the error messages are deterministic. +//@compile-flags: -Zmiri-deterministic-concurrency +use std::ptr::addr_of_mut; +use std::sync::{Arc, Barrier}; +use std::thread; + +#[derive(Copy, Clone)] +struct SendPtr(*mut u8); + +unsafe impl Send for SendPtr {} + +fn thread_1(x: SendPtr, barrier: Arc) { + let x = unsafe { &mut *x.0 }; + barrier.wait(); // init + + let _v = *x; + + barrier.wait(); // write +} + +fn thread_2(y: SendPtr, barrier: Arc) { + let y = unsafe { &mut *y.0 }; + barrier.wait(); // init + thread::yield_now(); // force other thread to read first + + fn write(y: &mut u8, v: u8, barrier: &Arc) { + //~^ ERROR: /Undefined Behavior: Data race detected between .* non-atomic read on thread .* and .* retag write of type .* on thread .* at .*/ + barrier.wait(); // write + *y = v; + } + write(&mut *y, 42, &barrier); +} + +fn main() { + let mut data = 0u8; + let p = SendPtr(addr_of_mut!(data)); + let barrier = Arc::new(Barrier::new(2)); + let b1 = Arc::clone(&barrier); + let b2 = Arc::clone(&barrier); + + let h1 = thread::spawn(move || thread_1(p, b1)); + let h2 = thread::spawn(move || thread_2(p, b2)); + h1.join().unwrap(); + h2.join().unwrap(); +} diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/retag_is_race.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/retag_is_race.stderr new file mode 100644 index 0000000000000..bbf9e626a1637 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/retag_is_race.stderr @@ -0,0 +1,34 @@ +error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) retag write of type `u8` on thread `unnamed-ID` at ALLOC + --> tests/fail/tree_borrows/implicit_writes/retag_is_race.rs:LL:CC + | +LL | fn write(y: &mut u8, v: u8, barrier: &Arc) { + | ^ (2) just happened here + | +help: and (1) occurred earlier here + --> tests/fail/tree_borrows/implicit_writes/retag_is_race.rs:LL:CC + | +LL | let _v = *x; + | ^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: this is on thread `unnamed-ID` + = note: stack backtrace: + 0: thread_2::write + at tests/fail/tree_borrows/implicit_writes/retag_is_race.rs:LL:CC + 1: thread_2 + at tests/fail/tree_borrows/implicit_writes/retag_is_race.rs:LL:CC + 2: main::{closure#1} + at tests/fail/tree_borrows/implicit_writes/retag_is_race.rs:LL:CC +note: the last function in that backtrace got called indirectly due to this code + --> tests/fail/tree_borrows/implicit_writes/retag_is_race.rs:LL:CC + | +LL | let h2 = thread::spawn(move || thread_2(p, b2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/static_memory_modification.rs b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/static_memory_modification.rs new file mode 100644 index 0000000000000..e88cfdae2be89 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/static_memory_modification.rs @@ -0,0 +1,11 @@ +// Tests that inserting an implicit write to a read-only allocation generates the correct error message. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes +static X: usize = 5; + +#[allow(mutable_transmutes)] +fn main() { + let x = unsafe { std::mem::transmute::<&usize, &mut usize>(&X) }; + foo(x); +} + +fn foo(_x: &mut usize) {} //~ ERROR: writing to alloc1 which is read-only diff --git a/src/tools/miri/tests/fail/tree_borrows/implicit_writes/static_memory_modification.stderr b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/static_memory_modification.stderr new file mode 100644 index 0000000000000..fe6b230c4d3c0 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/implicit_writes/static_memory_modification.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: writing to ALLOC which is read-only + --> tests/fail/tree_borrows/implicit_writes/static_memory_modification.rs:LL:CC + | +LL | fn foo(_x: &mut usize) {} + | ^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: stack backtrace: + 0: foo + at tests/fail/tree_borrows/implicit_writes/static_memory_modification.rs:LL:CC + 1: main + at tests/fail/tree_borrows/implicit_writes/static_memory_modification.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 9dc1af1be299f..b2402ac482ffa 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -43,6 +43,8 @@ fn main() { #[cfg(target_os = "linux")] test_sync_file_range(); test_fstat(); + test_stat(); + test_lstat(); test_isatty(); test_read_and_uninit(); test_nofollow_not_symlink(); @@ -50,6 +52,147 @@ fn main() { test_ioctl(); test_opendir_closedir(); test_readdir(); + #[cfg(target_os = "linux")] + test_statx_on_file_path(); + #[cfg(target_os = "linux")] + test_statx_on_file_descriptor(); + #[cfg(target_os = "linux")] + test_statx_empty_path_on_pipe(); +} + +#[cfg(target_os = "linux")] +#[track_caller] +fn assert_statx_matches_metadata(stx: &libc::statx, meta: &std::fs::Metadata, expected_size: u64) { + use std::os::unix::fs::MetadataExt; + let mask = stx.stx_mask; + + // Guaranteed by the shim on any Linux target. + assert!(mask & libc::STATX_TYPE != 0); + assert!(mask & libc::STATX_SIZE != 0); + assert_eq!(stx.stx_size, expected_size); + assert_eq!((stx.stx_mode as u32) & libc::S_IFMT, libc::S_IFREG); + + // Host-dependent enrichment: only assert when the mask says the field is real. + if mask & libc::STATX_INO != 0 { + assert_eq!(stx.stx_ino, meta.ino()); + } + if mask & libc::STATX_NLINK != 0 { + assert_eq!(stx.stx_nlink as u64, meta.nlink()); + } + if mask & libc::STATX_UID != 0 { + assert_eq!(stx.stx_uid, meta.uid()); + } + if mask & libc::STATX_GID != 0 { + assert_eq!(stx.stx_gid, meta.gid()); + } + if mask & libc::STATX_BLOCKS != 0 { + assert_eq!(stx.stx_blocks, meta.blocks()); + } + + // We don't support non-S_IFMT bits in stx_mode. + assert_eq!(mask & libc::STATX_MODE, 0); + + // Do not assert stx_blksize and stx_dev_* : there are no mask bits for them. +} + +#[cfg(target_os = "linux")] +fn test_statx_on_file_descriptor() { + use std::mem::MaybeUninit; + + let bytes = b"hello"; + let path = utils::prepare_with_content("miri_test_libc_statx_fd.txt", bytes); + let file = File::open(&path).unwrap(); + + unsafe { + let mut stx = MaybeUninit::::zeroed(); + let ret = libc::statx( + file.as_raw_fd(), + c"".as_ptr(), + libc::AT_EMPTY_PATH, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + ); + assert_eq!(ret, 0, "statx failed: {}", std::io::Error::last_os_error()); + + let stx = stx.assume_init(); + let meta = file.metadata().unwrap(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + + 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"); + + unsafe { + let mut stx = MaybeUninit::::zeroed(); + let ret = libc::statx( + libc::AT_FDCWD, + c_path.as_ptr(), + 0, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + ); + assert_eq!(ret, 0, "statx failed: {}", std::io::Error::last_os_error()); + + let stx = stx.assume_init(); + let meta = std::fs::metadata(&path).unwrap(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + + remove_file(&path).unwrap(); +} + +#[cfg(target_os = "linux")] +fn test_statx_empty_path_on_pipe() { + use libc_utils::errno_check; + + unsafe { + let mut fds = [0; 2]; + errno_check(libc::pipe(fds.as_mut_ptr())); + + let mut statx_buf = std::mem::MaybeUninit::::zeroed(); + + let ret = libc::statx( + fds[0], + c"".as_ptr(), + libc::AT_EMPTY_PATH, + libc::STATX_BASIC_STATS, + statx_buf.as_mut_ptr(), + ); + + assert_eq!( + ret, + 0, + "statx on pipe with AT_EMPTY_PATH failed: {}", + std::io::Error::last_os_error() + ); + + let statx_buf = statx_buf.assume_init(); + + assert_ne!(statx_buf.stx_mask & libc::STATX_TYPE, 0); + assert_ne!(statx_buf.stx_mask & libc::STATX_SIZE, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_MODE, 0); + assert_eq!((statx_buf.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFIFO); + assert_eq!(statx_buf.stx_size, 0); + + // Synthetic metadata must not advertise host-only fields. + assert_eq!(statx_buf.stx_mask & libc::STATX_INO, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_NLINK, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_UID, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_GID, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_BLOCKS, 0); + + errno_check(libc::close(fds[0])); + errno_check(libc::close(fds[1])); + } } fn test_file_open_unix_allow_two_args() { @@ -464,24 +607,55 @@ fn test_fstat() { assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG); // Check that all fields are initialized. - let _st_nlink = stat.st_nlink; - let _st_blksize = stat.st_blksize; - let _st_blocks = stat.st_blocks; - let _st_ino = stat.st_ino; - let _st_dev = stat.st_dev; - let _st_uid = stat.st_uid; - let _st_gid = stat.st_gid; - let _st_rdev = stat.st_rdev; - let _st_atime = stat.st_atime; - let _st_mtime = stat.st_mtime; - let _st_ctime = stat.st_ctime; - let _st_atime_nsec = stat.st_atime_nsec; - let _st_mtime_nsec = stat.st_mtime_nsec; - let _st_ctime_nsec = stat.st_ctime_nsec; + check_stat_fields(stat); + + remove_file(&path).unwrap(); +} + +fn test_stat() { + use std::mem::MaybeUninit; + + let path = utils::prepare_with_content("miri_test_libc_stat.txt", b"hello"); + let cpath = CString::new(path.as_os_str().as_bytes()).unwrap(); + + let mut stat = MaybeUninit::::uninit(); + let res = unsafe { libc::stat(cpath.as_ptr(), stat.as_mut_ptr()) }; + assert_eq!(res, 0); + let stat = unsafe { stat.assume_init_ref() }; + + assert_eq!(stat.st_size, 5); + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG); + + // Check that all fields are initialized. + check_stat_fields(stat); remove_file(&path).unwrap(); } +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 mut stat = MaybeUninit::::uninit(); + let res = unsafe { libc::lstat(cpath.as_ptr(), stat.as_mut_ptr()) }; + assert_eq!(res, 0); + let stat = unsafe { stat.assume_init_ref() }; + + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFLNK); + + // Check that all fields are initialized. + check_stat_fields(stat); + + remove_file(&symlink_path).unwrap(); + remove_file(&path).unwrap(); +} + fn test_isatty() { // Testing whether our isatty shim returns the right value would require controlling whether // these streams are actually TTYs, which is hard. @@ -651,3 +825,21 @@ fn test_readdir() { remove_file(&file2).unwrap(); remove_dir(&dir_path).unwrap(); } + +/// Check that all common fields of a `stat` struct are initialized. +pub fn check_stat_fields(stat: &libc::stat) { + let _st_nlink = stat.st_nlink; + let _st_blksize = stat.st_blksize; + let _st_blocks = stat.st_blocks; + let _st_ino = stat.st_ino; + let _st_dev = stat.st_dev; + let _st_uid = stat.st_uid; + let _st_gid = stat.st_gid; + let _st_rdev = stat.st_rdev; + let _st_atime = stat.st_atime; + let _st_mtime = stat.st_mtime; + let _st_ctime = stat.st_ctime; + let _st_atime_nsec = stat.st_atime_nsec; + let _st_mtime_nsec = stat.st_mtime_nsec; + let _st_ctime_nsec = stat.st_ctime_nsec; +} 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 c87677a756a00..6f3bd22b42e6c 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -16,6 +16,7 @@ const TEST_BYTES: &[u8] = b"these are some test bytes!"; fn main() { test_create_close(); + test_create_close_tcp(); test_bind_ipv4(); test_bind_ipv4_reuseaddr(); test_set_reuseaddr_invalid_len(); @@ -59,6 +60,21 @@ fn test_create_close() { unsafe { errno_check(libc::close(sockfd)) }; } +/// Test creating a socket and then closing it afterwards but we explicitly +/// specify that the TCP protocol should be used. +fn test_create_close_tcp() { + let sockfd = unsafe { + errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, libc::IPPROTO_TCP)).unwrap() + }; + + let flags = unsafe { errno_result(libc::fcntl(sockfd, libc::F_GETFL, 0)).unwrap() }; + + // Ensure that socket is initially blocking. + assert_eq!(flags & libc::O_NONBLOCK, 0); + + unsafe { errno_check(libc::close(sockfd)) }; +} + fn test_bind_ipv4() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; diff --git a/src/tools/miri/tests/pass/associated-const.rs b/src/tools/miri/tests/pass/associated-const.rs index 331fbfcefde15..d33c1da8efd54 100644 --- a/src/tools/miri/tests/pass/associated-const.rs +++ b/src/tools/miri/tests/pass/associated-const.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows trait Foo { const ID: i32; diff --git a/src/tools/miri/tests/pass/assume_bug.rs b/src/tools/miri/tests/pass/assume_bug.rs index 662b90150886d..0d239fb43673c 100644 --- a/src/tools/miri/tests/pass/assume_bug.rs +++ b/src/tools/miri/tests/pass/assume_bug.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows fn main() { vec![()].into_iter(); diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs index 4fa84384d9bdd..ee47827bf0a72 100644 --- a/src/tools/miri/tests/pass/async-drop.rs +++ b/src/tools/miri/tests/pass/async-drop.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-strict-provenance //@[tree]compile-flags: -Zmiri-tree-borrows diff --git a/src/tools/miri/tests/pass/async-drop.tree_implicit_writes.stdout b/src/tools/miri/tests/pass/async-drop.tree_implicit_writes.stdout new file mode 100644 index 0000000000000..fc53df2f1b485 --- /dev/null +++ b/src/tools/miri/tests/pass/async-drop.tree_implicit_writes.stdout @@ -0,0 +1,23 @@ +AsyncInt::async_drop: 0 +AsyncInt::async_drop: 1 +AsyncInt::async_drop: 2 +AsyncInt::async_drop: 3 +AsyncInt::async_drop: 4 +AsyncStruct::async_drop: 6 +AsyncInt::async_drop: 7 +AsyncInt::async_drop: 8 +AsyncReference::async_drop: 10 +AsyncInt::async_drop: 11 +AsyncEnum(A)::async_drop: 12 +SyncInt::drop: 12 +AsyncEnum(B)::async_drop: 13 +AsyncInt::async_drop: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::async_drop: 16 +SyncInt::drop: 17 +AsyncInt::async_drop: 18 +AsyncInt::async_drop: 19 +AsyncInt::async_drop: 20 +AsyncUnion::async_drop: 21, 21 +AsyncInt::async_drop: 10 diff --git a/src/tools/miri/tests/pass/async-niche-aliasing.rs b/src/tools/miri/tests/pass/async-niche-aliasing.rs index fe4ddd9d80088..7ec8495f546ad 100644 --- a/src/tools/miri/tests/pass/async-niche-aliasing.rs +++ b/src/tools/miri/tests/pass/async-niche-aliasing.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::future::Future; diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs index 2231affc0c618..842f036b0615a 100644 --- a/src/tools/miri/tests/pass/atomic.rs +++ b/src/tools/miri/tests/pass/atomic.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance diff --git a/src/tools/miri/tests/pass/both_borrows/2phase.rs b/src/tools/miri/tests/pass/both_borrows/2phase.rs index 7a3962a7c1a44..b9c0e0ee46b2e 100644 --- a/src/tools/miri/tests/pass/both_borrows/2phase.rs +++ b/src/tools/miri/tests/pass/both_borrows/2phase.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows trait S: Sized { diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index 115e232dde4c2..d732a43686c9e 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(allocator_api)] use std::alloc::{Layout, alloc, dealloc}; diff --git a/src/tools/miri/tests/pass/both_borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/both_borrows/int-to-ptr.rs index 830feed7e2b01..58233c8a0036b 100644 --- a/src/tools/miri/tests/pass/both_borrows/int-to-ptr.rs +++ b/src/tools/miri/tests/pass/both_borrows/int-to-ptr.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-permissive-provenance //@[tree]compile-flags: -Zmiri-tree-borrows use std::ptr; diff --git a/src/tools/miri/tests/pass/both_borrows/interior_mutability.rs b/src/tools/miri/tests/pass/both_borrows/interior_mutability.rs index f095e215e0043..8327ad7e9e125 100644 --- a/src/tools/miri/tests/pass/both_borrows/interior_mutability.rs +++ b/src/tools/miri/tests/pass/both_borrows/interior_mutability.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![allow(dangerous_implicit_autorefs)] diff --git a/src/tools/miri/tests/pass/both_borrows/issue-miri-2389.rs b/src/tools/miri/tests/pass/both_borrows/issue-miri-2389.rs index f9d87b7dea8fe..f0b2c3b6f8580 100644 --- a/src/tools/miri/tests/pass/both_borrows/issue-miri-2389.rs +++ b/src/tools/miri/tests/pass/both_borrows/issue-miri-2389.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-permissive-provenance //@[tree]compile-flags: -Zmiri-tree-borrows use std::cell::Cell; diff --git a/src/tools/miri/tests/pass/both_borrows/maybe_dangling.rs b/src/tools/miri/tests/pass/both_borrows/maybe_dangling.rs index fe2b526d94b19..c3c290824acbe 100644 --- a/src/tools/miri/tests/pass/both_borrows/maybe_dangling.rs +++ b/src/tools/miri/tests/pass/both_borrows/maybe_dangling.rs @@ -1,7 +1,8 @@ // Check that `MaybeDangling` actually prevents UB when it wraps dangling // boxes and references // -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(maybe_dangling)] diff --git a/src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs b/src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs index 8e222e6279564..8f948cced3ed1 100644 --- a/src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs +++ b/src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(unsafe_pinned)] diff --git a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs index 633291182839f..06ddda278765a 100644 --- a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs +++ b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs @@ -2,7 +2,8 @@ //! If `Box` has a local allocator, then it can't be `noalias` as the allocator //! may want to access allocator state based on the data pointer. -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(allocator_api)] diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs index cfd01ce28719d..4822a496ccb60 100644 --- a/src/tools/miri/tests/pass/btreemap.rs +++ b/src/tools/miri/tests/pass/btreemap.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance use std::collections::{BTreeMap, BTreeSet}; diff --git a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs index adbf5df62cc5d..e5c5bbf9710e9 100644 --- a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs +++ b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows // Check that you can cast between different pointers to trait objects // whose vtable have the same kind (both lengths, or both trait pointers). diff --git a/src/tools/miri/tests/pass/concurrency/channels.rs b/src/tools/miri/tests/pass/concurrency/channels.rs index 43086756b035f..6c40cddbe8456 100644 --- a/src/tools/miri/tests/pass/concurrency/channels.rs +++ b/src/tools/miri/tests/pass/concurrency/channels.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance diff --git a/src/tools/miri/tests/pass/concurrency/sync.rs b/src/tools/miri/tests/pass/concurrency/sync.rs index 142ac9cc8ca49..f8cf161e5ace6 100644 --- a/src/tools/miri/tests/pass/concurrency/sync.rs +++ b/src/tools/miri/tests/pass/concurrency/sync.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows // We use `yield` to test specific interleavings, so disable automatic preemption. //@compile-flags: -Zmiri-disable-isolation -Zmiri-deterministic-concurrency diff --git a/src/tools/miri/tests/pass/concurrency/sync.tree_implicit_writes.stdout b/src/tools/miri/tests/pass/concurrency/sync.tree_implicit_writes.stdout new file mode 100644 index 0000000000000..f2c036a1735ed --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/sync.tree_implicit_writes.stdout @@ -0,0 +1,20 @@ +before wait +before wait +before wait +before wait +before wait +before wait +before wait +before wait +before wait +before wait +after wait +after wait +after wait +after wait +after wait +after wait +after wait +after wait +after wait +after wait diff --git a/src/tools/miri/tests/pass/coroutine.rs b/src/tools/miri/tests/pass/coroutine.rs index 96b60b515cb6d..5411ee889054e 100644 --- a/src/tools/miri/tests/pass/coroutine.rs +++ b/src/tools/miri/tests/pass/coroutine.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(coroutines, coroutine_trait, never_type, stmt_expr_attributes)] diff --git a/src/tools/miri/tests/pass/disable-alignment-check.rs b/src/tools/miri/tests/pass/disable-alignment-check.rs index e8c0e027673c2..33da2276a6c2e 100644 --- a/src/tools/miri/tests/pass/disable-alignment-check.rs +++ b/src/tools/miri/tests/pass/disable-alignment-check.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-disable-alignment-check -Cdebug-assertions=no diff --git a/src/tools/miri/tests/pass/disjoint-array-accesses.rs b/src/tools/miri/tests/pass/disjoint-array-accesses.rs index 50d0ea52ad4a1..59c615888128a 100644 --- a/src/tools/miri/tests/pass/disjoint-array-accesses.rs +++ b/src/tools/miri/tests/pass/disjoint-array-accesses.rs @@ -2,7 +2,8 @@ // unexpectedly caused borrowck errors for disjoint borrows of array elements, for which we had no // tests. This is a collection of a few code samples from that issue. -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows struct Test { diff --git a/src/tools/miri/tests/pass/dyn-arbitrary-self.rs b/src/tools/miri/tests/pass/dyn-arbitrary-self.rs index d993e5ad68ccc..48123d5607365 100644 --- a/src/tools/miri/tests/pass/dyn-arbitrary-self.rs +++ b/src/tools/miri/tests/pass/dyn-arbitrary-self.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(arbitrary_self_types_pointers, unsize, coerce_unsized, dispatch_from_dyn)] #![feature(rustc_attrs)] diff --git a/src/tools/miri/tests/pass/extern_types.rs b/src/tools/miri/tests/pass/extern_types.rs index 08e9caf4f1c11..4f53f31d613ff 100644 --- a/src/tools/miri/tests/pass/extern_types.rs +++ b/src/tools/miri/tests/pass/extern_types.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(extern_types)] diff --git a/src/tools/miri/tests/pass/future-self-referential.rs b/src/tools/miri/tests/pass/future-self-referential.rs index 88d52d8f1c1cc..6b46d6328fd0b 100644 --- a/src/tools/miri/tests/pass/future-self-referential.rs +++ b/src/tools/miri/tests/pass/future-self-referential.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::future::*; diff --git a/src/tools/miri/tests/pass/issues/issue-miri-3473.rs b/src/tools/miri/tests/pass/issues/issue-miri-3473.rs index 77b960c1cdcad..798ae989d5609 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-3473.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-3473.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::cell::UnsafeCell; diff --git a/src/tools/miri/tests/pass/linked-list.rs b/src/tools/miri/tests/pass/linked-list.rs index 36df30070cb50..fd4b4a89da019 100644 --- a/src/tools/miri/tests/pass/linked-list.rs +++ b/src/tools/miri/tests/pass/linked-list.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(linked_list_cursors)] use std::collections::LinkedList; diff --git a/src/tools/miri/tests/pass/many_shr_bor.rs b/src/tools/miri/tests/pass/many_shr_bor.rs index aa960aa147abe..a9a1f405d438d 100644 --- a/src/tools/miri/tests/pass/many_shr_bor.rs +++ b/src/tools/miri/tests/pass/many_shr_bor.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows // Make sure validation can handle many overlapping shared borrows for different parts of a data structure use std::cell::RefCell; diff --git a/src/tools/miri/tests/pass/memleak_ignored.rs b/src/tools/miri/tests/pass/memleak_ignored.rs index bba3207ee4cf1..9acc244d866d2 100644 --- a/src/tools/miri/tests/pass/memleak_ignored.rs +++ b/src/tools/miri/tests/pass/memleak_ignored.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-ignore-leaks diff --git a/src/tools/miri/tests/pass/option_box_transmute_ptr.rs b/src/tools/miri/tests/pass/option_box_transmute_ptr.rs index 0ba6607a5d442..c6e4f5de3634f 100644 --- a/src/tools/miri/tests/pass/option_box_transmute_ptr.rs +++ b/src/tools/miri/tests/pass/option_box_transmute_ptr.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows // This tests that the size of Option> is the same as *const i32. fn option_box_deref() -> i32 { diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs index 46f5bd73d55bb..d0df6de656d0c 100644 --- a/src/tools/miri/tests/pass/provenance.rs +++ b/src/tools/miri/tests/pass/provenance.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::{mem, ptr}; diff --git a/src/tools/miri/tests/pass/ptr_int_casts.rs b/src/tools/miri/tests/pass/ptr_int_casts.rs index 2d99a8a449b32..4debca85c686a 100644 --- a/src/tools/miri/tests/pass/ptr_int_casts.rs +++ b/src/tools/miri/tests/pass/ptr_int_casts.rs @@ -1,5 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::{mem, ptr}; diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs index 200e4b2ea3e94..f35b3591dfd31 100644 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs +++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs @@ -1,5 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::ptr; diff --git a/src/tools/miri/tests/pass/ptr_int_transmute.rs b/src/tools/miri/tests/pass/ptr_int_transmute.rs index d99c25413e640..2d2f1e7e55ad3 100644 --- a/src/tools/miri/tests/pass/ptr_int_transmute.rs +++ b/src/tools/miri/tests/pass/ptr_int_transmute.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows // Test what happens when we read parts of a pointer. // Related to . diff --git a/src/tools/miri/tests/pass/rc.rs b/src/tools/miri/tests/pass/rc.rs index ce01e611b2ced..bfc7fda11dbb5 100644 --- a/src/tools/miri/tests/pass/rc.rs +++ b/src/tools/miri/tests/pass/rc.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance #![feature(get_mut_unchecked)] diff --git a/src/tools/miri/tests/pass/send-is-not-static-par-for.rs b/src/tools/miri/tests/pass/send-is-not-static-par-for.rs index 458312508d2ec..5514f3b20e33d 100644 --- a/src/tools/miri/tests/pass/send-is-not-static-par-for.rs +++ b/src/tools/miri/tests/pass/send-is-not-static-par-for.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::sync::Mutex; diff --git a/src/tools/miri/tests/pass/shims/available-parallelism.rs b/src/tools/miri/tests/pass/shims/available-parallelism.rs index 77fb78424ba2d..1921bde3027b8 100644 --- a/src/tools/miri/tests/pass/shims/available-parallelism.rs +++ b/src/tools/miri/tests/pass/shims/available-parallelism.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows fn main() { assert_eq!(std::thread::available_parallelism().unwrap().get(), 1); diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 0417a4cbc6791..bd93804ab7a00 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -243,6 +243,33 @@ unsafe fn test_avx512() { } test_mm512_permutexvar_epi64(); + #[target_feature(enable = "avx512f")] + unsafe fn test_mm512_permutex2var_epi64() { + // a[i] = (i+1) * 10, b[i] = (i+1) * 100. + let a = _mm512_set_epi64(80, 70, 60, 50, 40, 30, 20, 10); + let b = _mm512_set_epi64(800, 700, 600, 500, 400, 300, 200, 100); + + // All from `a`: identity (bit 3 clear). + let idx = _mm512_set_epi64(7, 6, 5, 4, 3, 2, 1, 0); + assert_eq_m512i(_mm512_permutex2var_epi64(a, idx, b), a); + + // All from `b` (bit 3 set). + let idx = _mm512_set_epi64(15, 14, 13, 12, 11, 10, 9, 8); + assert_eq_m512i(_mm512_permutex2var_epi64(a, idx, b), b); + + // Interleave: even elements from `a`, odd from `b`. + let idx = _mm512_set_epi64(15, 6, 13, 4, 11, 2, 9, 0); + let e = _mm512_set_epi64(800, 70, 600, 50, 400, 30, 200, 10); + assert_eq_m512i(_mm512_permutex2var_epi64(a, idx, b), e); + + // Only the low 4 bits of each index are used: bits [2:0] pick the lane, + // bit 3 selects between `a` and `b`. + let idx = _mm512_set_epi64(0, -1, -128, i64::MIN, 15, 8, 128, i64::MAX); + let e = _mm512_set_epi64(10, 800, 10, 10, 800, 100, 10, 800); + assert_eq_m512i(_mm512_permutex2var_epi64(a, idx, b), e); + } + test_mm512_permutex2var_epi64(); + #[target_feature(enable = "avx512bw")] unsafe fn test_mm512_shuffle_epi8() { #[rustfmt::skip] diff --git a/src/tools/miri/tests/pass/strange_references.rs b/src/tools/miri/tests/pass/strange_references.rs index fe5ff93a9ca8d..8a483e330a9a4 100644 --- a/src/tools/miri/tests/pass/strange_references.rs +++ b/src/tools/miri/tests/pass/strange_references.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows // Create zero-sized references to vtables and function data. diff --git a/src/tools/miri/tests/pass/threadleak_ignored.rs b/src/tools/miri/tests/pass/threadleak_ignored.rs index d489985619487..e43b58cdbd158 100644 --- a/src/tools/miri/tests/pass/threadleak_ignored.rs +++ b/src/tools/miri/tests/pass/threadleak_ignored.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-ignore-leaks diff --git a/src/tools/miri/tests/pass/threadleak_ignored.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/threadleak_ignored.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..7557f49c7584b --- /dev/null +++ b/src/tools/miri/tests/pass/threadleak_ignored.tree_implicit_writes.stderr @@ -0,0 +1 @@ +Dropping 0 diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop.rs b/src/tools/miri/tests/pass/tls/tls_macro_drop.rs index 0d8a1cef511d8..0c0b9b7efee50 100644 --- a/src/tools/miri/tests/pass/tls/tls_macro_drop.rs +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::cell::RefCell; diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop.tree_implicit_writes.stdout b/src/tools/miri/tests/pass/tls/tls_macro_drop.tree_implicit_writes.stdout new file mode 100644 index 0000000000000..3e17acc832835 --- /dev/null +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop.tree_implicit_writes.stdout @@ -0,0 +1,8 @@ +Dropping: 8 (should be before 'Continue main 1'). +Dropping: 8 (should be before 'Continue main 1'). +Continue main 1. +Joining: 7 (should be before 'Continue main 2'). +Continue main 2. +Foo dtor (should be before `Bar dtor`). +Bar dtor (should be before `Continue main 3`). +Continue main 3. diff --git a/src/tools/miri/tests/pass/tls/tls_static.rs b/src/tools/miri/tests/pass/tls/tls_static.rs index cc4eaca72ab6e..600ecea334e13 100644 --- a/src/tools/miri/tests/pass/tls/tls_static.rs +++ b/src/tools/miri/tests/pass/tls/tls_static.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs index 0a53b77829473..4b7ac0f533832 100644 --- a/src/tools/miri/tests/pass/transmute_ptr.rs +++ b/src/tools/miri/tests/pass/transmute_ptr.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs index 019ea369811d9..84ce47b327dcc 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs @@ -1,4 +1,6 @@ // We disable the GC for this test because it would change what is printed. +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.stderr rename to src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..e09aed2cf5d01 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr @@ -0,0 +1,16 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| ReIM| └─┬── +|?Cel | ├──── +|?Cel | └──── +────────────────────────────────────────────────── +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Cel | ├──── +| Cel | └──── +────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs index ed8cbbf0e273b..eb8d7193ab970 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs @@ -1,3 +1,5 @@ +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows #![feature(box_as_ptr)] #[path = "../../utils/mod.rs"] diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/cell-inside-box.stderr rename to src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..5dbfff718b1e6 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr @@ -0,0 +1,7 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 4 +| Act | └─┬── +| Act | └─┬── +| ReIM| └──── +────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs index adeedc653b994..9ad9a149d280a 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs @@ -1,5 +1,7 @@ //! The same as `tests/fail/tree-borrows/cell-inside-struct` but with //! precise tracking of interior mutability disabled. +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-no-precise-interior-mut #[path = "../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.stderr rename to src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..1d939329040fa --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr @@ -0,0 +1,6 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 8 +| Act | └─┬── +|?Cel | └──── +────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs index 7352784ac7a5e..5aad810c323b0 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs @@ -1,3 +1,5 @@ +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows use std::cell::Cell; diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs index 261419b225b7d..0e2a1cb407c0c 100644 --- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs @@ -1,4 +1,6 @@ // We disable the GC for this test because it would change what is printed. +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 // Check that a protector goes back to normal behavior when the function diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.stderr b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/end-of-protector.stderr rename to src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..d33bfcdc0097a --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr @@ -0,0 +1,36 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Res | └─┬── +| Res | └──── +────────────────────────────────────────────────── +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Act | └─┬── +| Act | └─┬── +| Act | └──── Strongly protected +────────────────────────────────────────────────── +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Frz | ├─┬── +| Frz | │ └─┬── +| Frz | │ └──── +| Res | └──── +────────────────────────────────────────────────── +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Dis | ├─┬── +| Dis | │ └─┬── +| Dis | │ └──── +| Act | └──── +────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.rs b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.rs new file mode 100644 index 0000000000000..1b236347a7588 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.rs @@ -0,0 +1,17 @@ +// Tests that the permissions are as expected after reborrowing. +// To be precise, this tests that at the start of a function, a mutable reference is Unique. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +unsafe extern "Rust" { + safe fn miri_get_alloc_id(ptr: *const u8) -> u64; + safe fn miri_print_borrow_state(alloc_id: u64, show_unnamed: bool); +} + +fn bar(x: &mut u8) { + miri_print_borrow_state(miri_get_alloc_id(x), true); +} + +fn main() { + let mut x = 0u8; + bar(&mut x); +} diff --git a/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr new file mode 100644 index 0000000000000..1dd871530caad --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr @@ -0,0 +1,7 @@ +────────────────────────────────────────────── +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Act | └─┬── +| Act | └──── Strongly protected +────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/no_implicit_writes.rs b/src/tools/miri/tests/pass/tree_borrows/no_implicit_writes.rs new file mode 100644 index 0000000000000..8036a57686bb4 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/no_implicit_writes.rs @@ -0,0 +1,55 @@ +// Shows that without implicit writes, `tests/fail/tree_borrows/implicit_writes/ptr_write.rs`, `tests/fail/tree_borrows/implicit_writes/ptr_write_unsafe_cell.rs`, `tests/fail/tree_borrows/implicit_writes/ptr_write_box.rs`, `tests/fail/tree_borrows/implicit_writes/as_mut_ptr.rs` would pass. +//@compile-flags: -Zmiri-tree-borrows + +use std::cell::UnsafeCell; + +fn main() { + normal(); + unsafe_cell(); + box_test(); + as_mut_ptr_test() +} + +fn normal() { + let mut x = 0u8; + let ptr = &raw mut x; + let res = dereference(&mut x, ptr); + assert_eq!(*res, 0); +} + +fn unsafe_cell() { + let mut x: UnsafeCell = 0u8.into(); + let ptr = x.get(); + let res = dereference(&mut x, ptr); + assert_eq!(*res.get_mut(), 0); +} + +fn box_test() { + let mut x: Box = Box::new(0u8); + let ptr = &raw mut *x; + let res = dereference(x, ptr); + assert_eq!(*res, 0); +} + +fn dereference(x: T, y: *mut u8) -> T { + // miri inserts an implicit write here + let _ = unsafe { *y }; + // x = 42; // we'd like to add this write, so there must already be UB without it because there sure is with it. + x +} + +fn as_mut_ptr_test() { + let mut x: [u8; 3] = [1, 2, 3]; + + let ptr = std::ptr::from_mut(&mut x); + let a = unsafe { &mut *ptr }; + let b = unsafe { &mut *ptr }; + + let _c = as_mut_ptr(a); + assert_eq!(*b, [1, 2, 3]); +} + +// this should be the same as the implementation for slice, as this is known to cause errors and we want to test that behavior here +const fn as_mut_ptr(x: &mut [u8; 3]) -> *mut u8 { + x as *mut [u8] as *mut u8 +} diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs index edd649b91edd3..8e966a538d01e 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs @@ -1,4 +1,6 @@ // We disable the GC for this test because it would change what is printed. +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../utils/mod.rs"] diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.stderr b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.stderr rename to src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr new file mode 100644 index 0000000000000..d589a06211182 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr @@ -0,0 +1,15 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Act | └──── +────────────────────────────────────────────────── +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act | └─┬── +| Act | └─┬── +| Frz | ├──── +| Res | └──── +────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/read_retag_no_race.rs b/src/tools/miri/tests/pass/tree_borrows/retag_no_race.rs similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/read_retag_no_race.rs rename to src/tools/miri/tests/pass/tree_borrows/retag_no_race.rs diff --git a/src/tools/miri/tests/pass/tree_borrows/read_retag_no_race.stderr b/src/tools/miri/tests/pass/tree_borrows/retag_no_race.stderr similarity index 100% rename from src/tools/miri/tests/pass/tree_borrows/read_retag_no_race.stderr rename to src/tools/miri/tests/pass/tree_borrows/retag_no_race.stderr diff --git a/src/tools/miri/tests/pass/tree_borrows/transmute-unsafecell.rs b/src/tools/miri/tests/pass/tree_borrows/transmute-unsafecell.rs index 1df0636e1e5d2..6bc9d1a5d14c7 100644 --- a/src/tools/miri/tests/pass/tree_borrows/transmute-unsafecell.rs +++ b/src/tools/miri/tests/pass/tree_borrows/transmute-unsafecell.rs @@ -1,3 +1,5 @@ +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows //! Testing `mem::transmute` between types with and without interior mutability. diff --git a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs index 0ed1dbacc7070..4bcaf823e99ca 100644 --- a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs +++ b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs @@ -1,3 +1,5 @@ +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows #![feature(allocator_api)] @@ -12,6 +14,7 @@ fn main() { direct_mut_to_const_raw(); local_addr_of_mut(); returned_mut_is_usable(); + array_pointer_access(); } #[allow(unused_assignments)] @@ -102,3 +105,33 @@ fn direct_mut_to_const_raw() { } assert_eq!(*x, 1); } + +// Tests that accessing the same array using disjoint pointers does not cause UB under Tree Borrows. +// The pointer will access the memory range using the outside permission, thus testing if it has been set correctly. +// In particular, in implicit_writes mode we used to set the "outside" permissions to +// `Unique`, which caused an ICE. +// Put differently, this checks the absence of subobject provenance for arrays. +fn array_pointer_access() { + let mut x = [1u8; 11]; + let y = (&raw mut x).cast(); + let res = foo(&mut x[0], y); + assert_eq!(res, 20); +} + +fn foo(x: &mut u8, y: *mut u8) -> u8 { + unsafe { + let x: *mut u8 = x; + let res1 = access_nexts(x.add(1), y.add(1)); + let res2 = access_nexts(y.add(1), x.add(1)); + res1 + res2 + } +} + +fn access_nexts(x: *mut u8, y: *mut u8) -> u8 { + let mut sum = 0; + for i in 0..5 { + sum += unsafe { *x.add(2 * i) }; + sum += unsafe { *y.add(2 * i + 1) }; + } + sum +} diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs b/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs index fbb50d5acd27a..a9dcb7b3defdb 100644 --- a/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs +++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs @@ -1,3 +1,5 @@ +//@revisions: tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance pub fn main() { diff --git a/src/tools/miri/tests/pass/unsized.rs b/src/tools/miri/tests/pass/unsized.rs index 1e62cd7f3239e..a59d7df9b8fce 100644 --- a/src/tools/miri/tests/pass/unsized.rs +++ b/src/tools/miri/tests/pass/unsized.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(unsized_fn_params)] #![feature(custom_mir, core_intrinsics)] diff --git a/src/tools/miri/tests/pass/vecdeque.rs b/src/tools/miri/tests/pass/vecdeque.rs index bdf57f281a08f..67fe5beacd328 100644 --- a/src/tools/miri/tests/pass/vecdeque.rs +++ b/src/tools/miri/tests/pass/vecdeque.rs @@ -1,4 +1,5 @@ -//@revisions: stack tree +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-strict-provenance //@[tree]compile-flags: -Zmiri-tree-borrows use std::collections::VecDeque; diff --git a/tests/codegen-llvm/cffi/c-variadic-naked.rs b/tests/codegen-llvm/cffi/c-variadic-naked.rs index 5843628b6330a..caca6d327dd63 100644 --- a/tests/codegen-llvm/cffi/c-variadic-naked.rs +++ b/tests/codegen-llvm/cffi/c-variadic-naked.rs @@ -1,5 +1,5 @@ //@ needs-asm-support -//@ only-x86_64 +//@ needs-asm-mnemonic: ret // tests that `va_start` is not injected into naked functions diff --git a/tests/codegen-llvm/naked-fn/aligned.rs b/tests/codegen-llvm/naked-fn/aligned.rs index d7281c4219a10..8c4ac57a7bf90 100644 --- a/tests/codegen-llvm/naked-fn/aligned.rs +++ b/tests/codegen-llvm/naked-fn/aligned.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 //@ needs-asm-support -//@ ignore-arm no "ret" mnemonic +//@ needs-asm-mnemonic: ret //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) #![crate_type = "lib"] diff --git a/tests/codegen-llvm/naked-fn/min-function-alignment.rs b/tests/codegen-llvm/naked-fn/min-function-alignment.rs index 406e9334fa59c..2619f4ef476a7 100644 --- a/tests/codegen-llvm/naked-fn/min-function-alignment.rs +++ b/tests/codegen-llvm/naked-fn/min-function-alignment.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmin-function-alignment=16 //@ needs-asm-support -//@ ignore-arm no "ret" mnemonic +//@ needs-asm-mnemonic: ret //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity diff --git a/tests/run-make/archive-corrupt-error/corrupt.a b/tests/run-make/archive-corrupt-error/corrupt.a new file mode 100644 index 0000000000000..1e6232305b839 --- /dev/null +++ b/tests/run-make/archive-corrupt-error/corrupt.a @@ -0,0 +1,3 @@ +! +corrupt.o/ 0 0 0 100644 10000 ` +small_data diff --git a/tests/run-make/archive-corrupt-error/lib.rs b/tests/run-make/archive-corrupt-error/lib.rs new file mode 100644 index 0000000000000..9412884815b74 --- /dev/null +++ b/tests/run-make/archive-corrupt-error/lib.rs @@ -0,0 +1,3 @@ +extern "C" { + fn foo() -> i32; +} diff --git a/tests/run-make/archive-corrupt-error/rmake.rs b/tests/run-make/archive-corrupt-error/rmake.rs new file mode 100644 index 0000000000000..d18659ca638a4 --- /dev/null +++ b/tests/run-make/archive-corrupt-error/rmake.rs @@ -0,0 +1,21 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148217 +// A corrupt archive with member offset exceeding file boundary should produce +// an error, not an ICE. + +//@ ignore-cross-compile + +use run_make_support::{path, rfs, rustc, static_lib_name}; + +fn main() { + rfs::create_dir("archive"); + rfs::copy("corrupt.a", path("archive").join(static_lib_name("corrupt"))); + rustc() + .input("lib.rs") + .crate_type("rlib") + .library_search_path("archive") + .arg("-lstatic=corrupt") + .run_fail() + .assert_stderr_not_contains("panicked") + .assert_stderr_not_contains("unexpectedly panicked") + .assert_stderr_contains("invalid archive member"); +} diff --git a/tests/run-make/archive-format-error/lib.rs b/tests/run-make/archive-format-error/lib.rs new file mode 100644 index 0000000000000..9412884815b74 --- /dev/null +++ b/tests/run-make/archive-format-error/lib.rs @@ -0,0 +1,3 @@ +extern "C" { + fn foo() -> i32; +} diff --git a/tests/run-make/archive-format-error/native.c b/tests/run-make/archive-format-error/native.c new file mode 100644 index 0000000000000..bf7759e11ea9d --- /dev/null +++ b/tests/run-make/archive-format-error/native.c @@ -0,0 +1 @@ +int foo() { return 42; } diff --git a/tests/run-make/archive-format-error/rmake.rs b/tests/run-make/archive-format-error/rmake.rs new file mode 100644 index 0000000000000..1d217f05b8d36 --- /dev/null +++ b/tests/run-make/archive-format-error/rmake.rs @@ -0,0 +1,22 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148217 +// BSD format archive on a Linux target should emit a format mismatch warning. + +//@ ignore-cross-compile +//@ only-linux + +use run_make_support::{cc, llvm_ar, path, rfs, rustc, static_lib_name}; + +fn main() { + rfs::create_dir("archive"); + + cc().arg("-c").input("native.c").output("archive/native.o").run(); + let bsd_archive = path("archive").join(static_lib_name("native_bsd")); + llvm_ar().arg("rcus").arg("--format=bsd").output_input(&bsd_archive, "archive/native.o").run(); + rustc() + .input("lib.rs") + .crate_type("rlib") + .library_search_path("archive") + .arg("-lstatic=native_bsd") + .run() + .assert_stderr_contains("was built as BSD format, but the target expects GNU"); +} diff --git a/tests/run-make/raw-dylib-custom-dlltool/script.cmd b/tests/run-make/raw-dylib-custom-dlltool/script.cmd index 51834590be034..c88878151af48 100644 --- a/tests/run-make/raw-dylib-custom-dlltool/script.cmd +++ b/tests/run-make/raw-dylib-custom-dlltool/script.cmd @@ -1,2 +1,2 @@ -echo Called dlltool via script.cmd> actual.txt +echo Called dlltool via script.cmd> %~dp0\actual.txt dlltool.exe %* diff --git a/tests/run-make/raw-dylib-whitespace/main.rs b/tests/run-make/raw-dylib-whitespace/main.rs new file mode 100644 index 0000000000000..023c3570c3262 --- /dev/null +++ b/tests/run-make/raw-dylib-whitespace/main.rs @@ -0,0 +1,18 @@ +type BOOL = i32; + +#[cfg_attr( + target_arch = "x86", + link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated") +)] +#[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))] +extern "system" { + fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; +} + +fn main() { + let mut num: u8 = 0; + unsafe { + ProcessPrng(&mut num, 1); + } + println!("{num}"); +} diff --git a/tests/run-make/raw-dylib-whitespace/rmake.rs b/tests/run-make/raw-dylib-whitespace/rmake.rs new file mode 100644 index 0000000000000..4f90eeb055142 --- /dev/null +++ b/tests/run-make/raw-dylib-whitespace/rmake.rs @@ -0,0 +1,15 @@ +// Ensure that raw-dylib still works if the output directory contains spaces. + +//@ only-windows-gnu +//@ ignore-cross-compile +//@ needs-dlltool +// Reason: this test specifically checks the dlltool feature, +// which is only used on windows-gnu. + +use run_make_support::{rfs, rustc}; + +fn main() { + let out_dir = std::path::absolute("path with spaces").unwrap(); + rfs::create_dir_all(&out_dir); + rustc().crate_type("bin").input("main.rs").out_dir(&out_dir).env("TMP", &out_dir).run(); +} diff --git a/tests/rustdoc-html/doc-cfg/extern-items.rs b/tests/rustdoc-html/doc-cfg/extern-items.rs new file mode 100644 index 0000000000000..369f8a7b22ee9 --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/extern-items.rs @@ -0,0 +1,26 @@ +// Ensure that the `cfg` on the extern blocks are correctly taken into account by +// their children. +// Regression test for . + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +//@has 'foo/index.html' +//@count - '//*[@class="stab portability"]' 2 +//@has - '//*[@class="stab portability"]' 'Non-banana' + +//@has 'foo/fn.doc_cfg_doesnt_work.html' +//@has - '//*[@class="stab portability"]' 'Available on non-crate feature banana only.' + +//@has 'foo/fn.doc_cfg_works.html' +//@has - '//*[@class="stab portability"]' 'Available on non-crate feature banana only.' + +unsafe extern "C" { + #[cfg(not(feature = "banana"))] + pub fn doc_cfg_works(); +} + +#[cfg(not(feature = "banana"))] +unsafe extern "C" { + pub fn doc_cfg_doesnt_work(); +} diff --git a/tests/ui/cfg/cfg-target-compact-errors.rs b/tests/ui/cfg/cfg-target-compact-errors.rs index 1ce68330c7623..c674232d1d373 100644 --- a/tests/ui/cfg/cfg-target-compact-errors.rs +++ b/tests/ui/cfg/cfg-target-compact-errors.rs @@ -4,22 +4,35 @@ #[cfg(target(o::o))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected a valid identifier here +//~| NOTE for more information, visit +//~| ERROR malformed `cfg` attribute input +//~| NOTE expected this to be of the form `... = "..."` +//~| NOTE for more information, visit fn one() {} #[cfg(target(os = 8))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected a string literal here +//~| NOTE for more information, visit fn two() {} #[cfg(target(os = "linux", pointer(width = "64")))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected this to be of the form `... = "..."` +//~| NOTE for more information, visit fn three() {} #[cfg(target(true))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected this to be of the form `... = "..."` +//~| NOTE for more information, visit fn four() {} #[cfg(target(clippy::os = "linux"))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE for more information, visit +//~| NOTE expected a valid identifier here fn five() {} fn main() {} diff --git a/tests/ui/cfg/cfg-target-compact-errors.stderr b/tests/ui/cfg/cfg-target-compact-errors.stderr index 6152b991015ef..a228e473e6032 100644 --- a/tests/ui/cfg/cfg-target-compact-errors.stderr +++ b/tests/ui/cfg/cfg-target-compact-errors.stderr @@ -1,3 +1,18 @@ +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-target-compact-errors.rs:5:1 + | +LL | #[cfg(target(o::o))] + | ^^^^^^^^^^^^^----^^^ + | | + | expected a valid identifier here + | + = note: for more information, visit +help: must be of the form + | +LL - #[cfg(target(o::o))] +LL + #[cfg(predicate)] + | + error[E0539]: malformed `cfg` attribute input --> $DIR/cfg-target-compact-errors.rs:5:1 | @@ -14,7 +29,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:9:1 + --> $DIR/cfg-target-compact-errors.rs:14:1 | LL | #[cfg(target(os = 8))] | ^^^^^^^^^^^^^^^^^^-^^^ @@ -29,7 +44,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:13:1 + --> $DIR/cfg-target-compact-errors.rs:20:1 | LL | #[cfg(target(os = "linux", pointer(width = "64")))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------^^^ @@ -44,7 +59,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:17:1 + --> $DIR/cfg-target-compact-errors.rs:26:1 | LL | #[cfg(target(true))] | ^^^^^^^^^^^^^----^^^ @@ -59,7 +74,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:21:1 + --> $DIR/cfg-target-compact-errors.rs:32:1 | LL | #[cfg(target(clippy::os = "linux"))] | ^^^^^^^^^^^^^----------^^^^^^^^^^^^^ @@ -73,6 +88,6 @@ LL - #[cfg(target(clippy::os = "linux"))] LL + #[cfg(predicate)] | -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/closures/auxiliary/wrong-closure-arg-suggestion-aux.rs b/tests/ui/closures/auxiliary/wrong-closure-arg-suggestion-aux.rs new file mode 100644 index 0000000000000..c4f3ef336f3a1 --- /dev/null +++ b/tests/ui/closures/auxiliary/wrong-closure-arg-suggestion-aux.rs @@ -0,0 +1,34 @@ +pub struct P; +pub struct Map(pub F); + +pub trait PIter: Sized { + fn map(self, f: F) -> Map + where + F: Fn(i32); + + fn flat_map(self, f: F) + where + F: Fn(i32) -> U; +} + +impl PIter for P { + fn map(self, f: F) -> Map + where + F: Fn(i32), + { + Map(f) + } + + fn flat_map(self, _: F) + where + F: Fn(i32) -> U, + { + } +} + +pub fn to_fn(f: F) -> Map +where + F: Fn(i32), +{ + Map(f) +} diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs index c72d19e84816d..ee4570be7fa63 100644 --- a/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs +++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs @@ -1,3 +1,5 @@ +//@ aux-build:wrong-closure-arg-suggestion-aux.rs + // Regression test for #125325 // Tests that we suggest changing an `impl Fn` param @@ -6,6 +8,10 @@ // Ensures that it works that way for both // functions and methods +extern crate wrong_closure_arg_suggestion_aux as aux; + +use aux::{P, PIter, to_fn}; + struct S; impl S { @@ -26,4 +32,37 @@ fn test_func(s: &S) -> usize { //~^ ERROR cannot assign to `x`, as it is a captured variable in a `Fn` closure } +// Regression test for . +// +// When the relevant `Fn` bound comes from a non-local callee, we should still +// explain the call-site expectation instead of falling back to the enclosing +// function's return type. +struct Counter { + counter: i32, +} + +impl Counter { + fn external_fn(mut self) -> i32 { + take(|_| to_fn(|_| self.counter += 1)); + //~^ ERROR cannot assign to `self.counter`, as `Fn` closures cannot mutate their captured variables [E0594] + //~| ERROR lifetime may not live long enough + //~| ERROR cannot borrow `self` as mutable, as it is a captured variable in a `Fn` closure [E0596] + self.counter + } + + fn external_method(mut self) -> i32 { + P.flat_map(|_| P.map(|_| self.counter += 1)); + //~^ ERROR cannot assign to `self.counter`, as `Fn` closures cannot mutate their captured variables [E0594] + //~| ERROR lifetime may not live long enough + //~| ERROR cannot borrow `self` as mutable, as it is a captured variable in a `Fn` closure [E0596] + self.counter + } +} + +fn take(_: F) +where + F: Fn(i32) -> U, +{ +} + fn main() {} diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr b/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr index f419f7c1b44d3..80da4e7145f95 100644 --- a/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr +++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr @@ -1,5 +1,5 @@ error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure - --> $DIR/wrong-closure-arg-suggestion-125325.rs:23:21 + --> $DIR/wrong-closure-arg-suggestion-125325.rs:29:21 | LL | fn assoc_func(&self, _f: impl Fn()) -> usize { | --------- change this to accept `FnMut` instead of `Fn` @@ -14,7 +14,7 @@ LL | s.assoc_func(|| x = ()); | expects `Fn` instead of `FnMut` error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure - --> $DIR/wrong-closure-arg-suggestion-125325.rs:25:13 + --> $DIR/wrong-closure-arg-suggestion-125325.rs:31:13 | LL | fn func(_f: impl Fn()) -> usize { | --------- change this to accept `FnMut` instead of `Fn` @@ -28,6 +28,76 @@ LL | func(|| x = ()) | | in this closure | expects `Fn` instead of `FnMut` -error: aborting due to 2 previous errors +error[E0594]: cannot assign to `self.counter`, as `Fn` closures cannot mutate their captured variables + --> $DIR/wrong-closure-arg-suggestion-125325.rs:46:28 + | +LL | take(|_| to_fn(|_| self.counter += 1)); + | ----- --- ^^^^^^^^^^^^^^^^^ cannot assign + | | | + | | in this closure + | expects `Fn` instead of `FnMut` + +error: lifetime may not live long enough + --> $DIR/wrong-closure-arg-suggestion-125325.rs:46:18 + | +LL | take(|_| to_fn(|_| self.counter += 1)); + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure `aux::Map<{closure@$DIR/wrong-closure-arg-suggestion-125325.rs:46:24: 46:27}>` contains a lifetime `'2` + | lifetime `'1` represents this closure's body + | + = note: closure implements `Fn`, so references to captured variables can't escape the closure + +error[E0596]: cannot borrow `self` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/wrong-closure-arg-suggestion-125325.rs:46:24 + | +LL | take(|_| to_fn(|_| self.counter += 1)); + | ---- --- ^^^ ------------ mutable borrow occurs due to use of `self` in closure + | | | | + | | | cannot borrow as mutable + | | in this closure + | expects `Fn` instead of `FnMut` +... +LL | fn take(_: F) + | - change this to accept `FnMut` instead of `Fn` + +error[E0594]: cannot assign to `self.counter`, as `Fn` closures cannot mutate their captured variables + --> $DIR/wrong-closure-arg-suggestion-125325.rs:54:34 + | +LL | P.flat_map(|_| P.map(|_| self.counter += 1)); + | --------^^^^^^^^^^^^^^^^^- + | | | | + | | | cannot assign + | | in this closure + | expects `Fn` instead of `FnMut` + +error: lifetime may not live long enough + --> $DIR/wrong-closure-arg-suggestion-125325.rs:54:24 + | +LL | P.flat_map(|_| P.map(|_| self.counter += 1)); + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure `aux::Map<{closure@$DIR/wrong-closure-arg-suggestion-125325.rs:54:30: 54:33}>` contains a lifetime `'2` + | lifetime `'1` represents this closure's body + | + = note: closure implements `Fn`, so references to captured variables can't escape the closure +help: consider adding 'move' keyword before the nested closure + | +LL | P.flat_map(|_| P.map(move |_| self.counter += 1)); + | ++++ + +error[E0596]: cannot borrow `self` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/wrong-closure-arg-suggestion-125325.rs:54:30 + | +LL | P.flat_map(|_| P.map(|_| self.counter += 1)); + | -------------------^^^-------------------- + | | | | | + | | | | mutable borrow occurs due to use of `self` in closure + | | | cannot borrow as mutable + | | in this closure + | expects `Fn` instead of `FnMut` + +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0594`. +Some errors have detailed explanations: E0594, E0596. +For more information about an error, try `rustc --explain E0594`. diff --git a/tests/ui/consts/const-eval/c-variadic-fail.rs b/tests/ui/consts/const-eval/c-variadic-fail.rs index a394de34a4804..063e1af88cc7c 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.rs +++ b/tests/ui/consts/const-eval/c-variadic-fail.rs @@ -6,7 +6,7 @@ #![feature(const_destruct)] #![feature(const_clone)] -use std::ffi::VaList; +use std::ffi::{VaList, c_char, c_void}; use std::mem::MaybeUninit; const unsafe extern "C" fn read_n(mut ap: ...) { @@ -37,7 +37,7 @@ const unsafe extern "C" fn read_as(mut ap: ...) -> T { ap.next_arg::() } -unsafe fn read_cast() { +unsafe fn read_cast_numeric() { const { read_as::(1i32) }; const { read_as::(1u32) }; @@ -47,20 +47,76 @@ unsafe fn read_cast() { const { read_as::(1i64) }; const { read_as::(1u64) }; + // A cast between signed and unsigned is OK so long as both types can represent the value. const { read_as::(1i32) }; - //~^ ERROR va_arg type mismatch: requested `u32`, but next argument is `i32` - const { read_as::(1u32) }; - //~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u32` + + type ConcreteUsize = cfg_select! { + target_pointer_width = "16" => u16, + target_pointer_width = "32" => u32, + target_pointer_width = "64" => u64, + }; + + type ConcreteIsize = cfg_select! { + target_pointer_width = "16" => i16, + target_pointer_width = "32" => i32, + target_pointer_width = "64" => i64, + }; + + const { read_as::(1usize) }; + const { read_as::(1 as ConcreteUsize) }; + + const { read_as::(-1isize) }; + const { read_as::(-1 as ConcreteIsize) }; + + const { read_as::(-1i32) }; + //~^ ERROR va_arg value mismatch: value `-1_i32` cannot be represented by type `u32` + const { read_as::(i32::MIN) }; + //~^ ERROR va_arg value mismatch: value `-2147483648_i32` cannot be represented by type `u32` + const { read_as::(u32::MAX) }; + //~^ ERROR va_arg value mismatch: value `4294967295_u32` cannot be represented by type `i32` + const { read_as::(i32::MAX as u32 + 1) }; + //~^ ERROR va_arg value mismatch: value `2147483648_u32` cannot be represented by type `i32` + const { read_as::(u64::MAX) }; + //~^ ERROR va_arg value mismatch: value `18446744073709551615_u64` cannot be represented by type `i64` + const { read_as::(i64::MAX as u64 + 1) }; + //~^ ERROR va_arg value mismatch: value `9223372036854775808_u64` cannot be represented by type `i64` const { read_as::(1u64) }; - //~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u64` + //~^ ERROR va_arg type mismatch: requested `i32` is incompatible with next argument of type `u64` const { read_as::(1i32) }; - //~^ ERROR va_arg type mismatch: requested `f64`, but next argument is `i32` + //~^ ERROR va_arg type mismatch: requested `f64` is incompatible with next argument of type `i32` +} - const { read_as::<*const u8>(1i32) }; - //~^ ERROR va_arg type mismatch: requested `*const u8`, but next argument is `i32` +unsafe fn read_cast_pointer() { + // A pointer mutability cast is OK. + const { read_as::<*const i32>(std::ptr::dangling_mut::()) }; + const { read_as::<*mut i32>(std::ptr::dangling::()) }; + + // A pointer cast is OK between compatible types. + const { read_as::<*const i32>(std::ptr::dangling::()) }; + const { read_as::<*const i32>(std::ptr::dangling_mut::()) }; + const { read_as::<*mut i32>(std::ptr::dangling::()) }; + const { read_as::<*mut i32>(std::ptr::dangling_mut::()) }; + + // Casting between pointers to i8/u8 and c_void is OK. + const { read_as::<*const c_char>(std::ptr::dangling::()) }; + const { read_as::<*const c_void>(std::ptr::dangling::()) }; + const { read_as::<*const i8>(std::ptr::dangling::()) }; + const { read_as::<*const c_void>(std::ptr::dangling::()) }; + const { read_as::<*const u8>(std::ptr::dangling::()) }; + const { read_as::<*const c_void>(std::ptr::dangling::()) }; + + const { read_as::<*const u16>(std::ptr::dangling::()) }; + //~^ ERROR va_arg type mismatch: requested `*const u16` is incompatible with next argument of type `*const c_void` + const { read_as::<*const c_void>(std::ptr::dangling::()) }; + //~^ ERROR va_arg type mismatch: requested `*const c_void` is incompatible with next argument of type `*const u16` + const { read_as::<*const u16>(std::ptr::dangling::()) }; + //~^ ERROR va_arg type mismatch: requested `*const u16` is incompatible with next argument of type `*const i32` + + const { read_as::<*const u8>(1usize) }; + //~^ ERROR requested `*const u8` is incompatible with next argument of type `usize` } fn use_after_free() { @@ -138,7 +194,8 @@ fn drop_of_invalid() { fn main() { unsafe { read_too_many(); - read_cast(); + read_cast_numeric(); + read_cast_pointer(); manual_copy_read(); manual_copy_drop(); manual_copy_forget(); diff --git a/tests/ui/consts/const-eval/c-variadic-fail.stderr b/tests/ui/consts/const-eval/c-variadic-fail.stderr index 4b0aed10d7647..d88ee9d6dd3c3 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.stderr +++ b/tests/ui/consts/const-eval/c-variadic-fail.stderr @@ -54,11 +54,11 @@ LL | const { read_n::<2>(1) } | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_arg type mismatch: requested `u32`, but next argument is `i32` - --> $DIR/c-variadic-fail.rs:50:13 +error[E0080]: va_arg value mismatch: value `-1_i32` cannot be represented by type `u32` + --> $DIR/c-variadic-fail.rs:72:13 | -LL | const { read_as::(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#6}` failed inside this call +LL | const { read_as::(-1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#12}` failed inside this call | note: inside `read_as::` --> $DIR/c-variadic-fail.rs:37:5 @@ -69,24 +69,52 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:50:5 + --> $DIR/c-variadic-fail.rs:72:5 | -LL | const { read_as::(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const { read_as::(-1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:50:5 + --> $DIR/c-variadic-fail.rs:72:5 | -LL | const { read_as::(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const { read_as::(-1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg value mismatch: value `-2147483648_i32` cannot be represented by type `u32` + --> $DIR/c-variadic-fail.rs:74:13 + | +LL | const { read_as::(i32::MIN) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#13}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:74:5 + | +LL | const { read_as::(i32::MIN) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:74:5 + | +LL | const { read_as::(i32::MIN) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u32` - --> $DIR/c-variadic-fail.rs:53:13 +error[E0080]: va_arg value mismatch: value `4294967295_u32` cannot be represented by type `i32` + --> $DIR/c-variadic-fail.rs:76:13 | -LL | const { read_as::(1u32) }; - | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#7}` failed inside this call +LL | const { read_as::(u32::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#14}` failed inside this call | note: inside `read_as::` --> $DIR/c-variadic-fail.rs:37:5 @@ -97,24 +125,108 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:53:5 + --> $DIR/c-variadic-fail.rs:76:5 | -LL | const { read_as::(1u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const { read_as::(u32::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:53:5 + --> $DIR/c-variadic-fail.rs:76:5 | -LL | const { read_as::(1u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const { read_as::(u32::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg value mismatch: value `2147483648_u32` cannot be represented by type `i32` + --> $DIR/c-variadic-fail.rs:78:13 + | +LL | const { read_as::(i32::MAX as u32 + 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#15}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:78:5 + | +LL | const { read_as::(i32::MAX as u32 + 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:78:5 + | +LL | const { read_as::(i32::MAX as u32 + 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg value mismatch: value `18446744073709551615_u64` cannot be represented by type `i64` + --> $DIR/c-variadic-fail.rs:80:13 + | +LL | const { read_as::(u64::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#16}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:80:5 + | +LL | const { read_as::(u64::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:80:5 + | +LL | const { read_as::(u64::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg value mismatch: value `9223372036854775808_u64` cannot be represented by type `i64` + --> $DIR/c-variadic-fail.rs:82:13 + | +LL | const { read_as::(i64::MAX as u64 + 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#17}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:82:5 + | +LL | const { read_as::(i64::MAX as u64 + 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:82:5 + | +LL | const { read_as::(i64::MAX as u64 + 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u64` - --> $DIR/c-variadic-fail.rs:56:13 +error[E0080]: va_arg type mismatch: requested `i32` is incompatible with next argument of type `u64` + --> $DIR/c-variadic-fail.rs:85:13 | LL | const { read_as::(1u64) }; - | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#8}` failed inside this call + | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#18}` failed inside this call | note: inside `read_as::` --> $DIR/c-variadic-fail.rs:37:5 @@ -125,24 +237,24 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:56:5 + --> $DIR/c-variadic-fail.rs:85:5 | LL | const { read_as::(1u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:56:5 + --> $DIR/c-variadic-fail.rs:85:5 | LL | const { read_as::(1u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_arg type mismatch: requested `f64`, but next argument is `i32` - --> $DIR/c-variadic-fail.rs:59:13 +error[E0080]: va_arg type mismatch: requested `f64` is incompatible with next argument of type `i32` + --> $DIR/c-variadic-fail.rs:88:13 | LL | const { read_as::(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#9}` failed inside this call + | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_numeric::{constant#19}` failed inside this call | note: inside `read_as::` --> $DIR/c-variadic-fail.rs:37:5 @@ -153,24 +265,108 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:59:5 + --> $DIR/c-variadic-fail.rs:88:5 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:59:5 + --> $DIR/c-variadic-fail.rs:88:5 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_arg type mismatch: requested `*const u8`, but next argument is `i32` - --> $DIR/c-variadic-fail.rs:62:13 +error[E0080]: va_arg type mismatch: requested `*const u16` is incompatible with next argument of type `*const c_void` + --> $DIR/c-variadic-fail.rs:111:13 + | +LL | const { read_as::<*const u16>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_pointer::{constant#12}` failed inside this call + | +note: inside `read_as::<*const u16>` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::<*const u16>` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:111:5 + | +LL | const { read_as::<*const u16>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:111:5 + | +LL | const { read_as::<*const u16>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `*const c_void` is incompatible with next argument of type `*const u16` + --> $DIR/c-variadic-fail.rs:113:13 + | +LL | const { read_as::<*const c_void>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_pointer::{constant#13}` failed inside this call | -LL | const { read_as::<*const u8>(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#10}` failed inside this call +note: inside `read_as::<*const c_void>` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::<*const c_void>` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:113:5 + | +LL | const { read_as::<*const c_void>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:113:5 + | +LL | const { read_as::<*const c_void>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `*const u16` is incompatible with next argument of type `*const i32` + --> $DIR/c-variadic-fail.rs:115:13 + | +LL | const { read_as::<*const u16>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_pointer::{constant#14}` failed inside this call + | +note: inside `read_as::<*const u16>` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::<*const u16>` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:115:5 + | +LL | const { read_as::<*const u16>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:115:5 + | +LL | const { read_as::<*const u16>(std::ptr::dangling::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `*const u8` is incompatible with next argument of type `usize` + --> $DIR/c-variadic-fail.rs:118:13 + | +LL | const { read_as::<*const u8>(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_pointer::{constant#15}` failed inside this call | note: inside `read_as::<*const u8>` --> $DIR/c-variadic-fail.rs:37:5 @@ -181,21 +377,21 @@ note: inside `VaList::<'_>::next_arg::<*const u8>` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:62:5 + --> $DIR/c-variadic-fail.rs:118:5 | -LL | const { read_as::<*const u8>(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const { read_as::<*const u8>(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:62:5 + --> $DIR/c-variadic-fail.rs:118:5 | -LL | const { read_as::<*const u8>(1i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const { read_as::<*const u8>(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling - --> $DIR/c-variadic-fail.rs:75:13 + --> $DIR/c-variadic-fail.rs:131:13 | LL | ap.next_arg::(); | ^^^^^^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call @@ -204,7 +400,7 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:71:5 + --> $DIR/c-variadic-fail.rs:127:5 | LL | / const { LL | | unsafe { @@ -215,7 +411,7 @@ LL | | }; | |_____^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:71:5 + --> $DIR/c-variadic-fail.rs:127:5 | LL | / const { LL | | unsafe { @@ -228,13 +424,13 @@ LL | | }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: using ALLOC1 as variable argument list pointer but it does not point to a variable argument list - --> $DIR/c-variadic-fail.rs:97:22 + --> $DIR/c-variadic-fail.rs:153:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_drop::{constant#0}` failed inside this call | note: inside `manual_copy_drop::helper` - --> $DIR/c-variadic-fail.rs:94:9 + --> $DIR/c-variadic-fail.rs:150:9 | LL | drop(ap); | ^^^^^^^^ @@ -246,13 +442,13 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:97:5 + --> $DIR/c-variadic-fail.rs:153:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:97:5 + --> $DIR/c-variadic-fail.rs:153:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -260,13 +456,13 @@ LL | const { unsafe { helper(1, 2, 3) } }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: using ALLOC2 as variable argument list pointer but it does not point to a variable argument list - --> $DIR/c-variadic-fail.rs:113:22 + --> $DIR/c-variadic-fail.rs:169:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_forget::{constant#0}` failed inside this call | note: inside `manual_copy_forget::helper` - --> $DIR/c-variadic-fail.rs:110:9 + --> $DIR/c-variadic-fail.rs:166:9 | LL | drop(ap); | ^^^^^^^^ @@ -278,13 +474,13 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:113:5 + --> $DIR/c-variadic-fail.rs:169:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:113:5 + --> $DIR/c-variadic-fail.rs:169:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -292,13 +488,13 @@ LL | const { unsafe { helper(1, 2, 3) } }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: using ALLOC3 as variable argument list pointer but it does not point to a variable argument list - --> $DIR/c-variadic-fail.rs:126:22 + --> $DIR/c-variadic-fail.rs:182:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_read::{constant#0}` failed inside this call | note: inside `manual_copy_read::helper` - --> $DIR/c-variadic-fail.rs:123:17 + --> $DIR/c-variadic-fail.rs:179:17 | LL | let _ = ap.next_arg::(); | ^^^^^^^^^^^^^^^^^^^^ @@ -306,13 +502,13 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:126:5 + --> $DIR/c-variadic-fail.rs:182:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:126:5 + --> $DIR/c-variadic-fail.rs:182:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -320,7 +516,7 @@ LL | const { unsafe { helper(1, 2, 3) } }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got null pointer - --> $DIR/c-variadic-fail.rs:134:5 + --> $DIR/c-variadic-fail.rs:190:5 | LL | } | ^ evaluation of `drop_of_invalid::{constant#0}` failed inside this call @@ -331,7 +527,7 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:131:5 + --> $DIR/c-variadic-fail.rs:187:5 | LL | / const { LL | | let mut invalid: MaybeUninit = MaybeUninit::zeroed(); @@ -340,7 +536,7 @@ LL | | } | |_____^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:131:5 + --> $DIR/c-variadic-fail.rs:187:5 | LL | / const { LL | | let mut invalid: MaybeUninit = MaybeUninit::zeroed(); @@ -350,6 +546,6 @@ LL | | } | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 12 previous errors +error: aborting due to 19 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/drop-impl-nonconst-drop-field.stderr b/tests/ui/consts/drop-impl-nonconst-drop-field.stderr index 0e626176579c6..5a81044dfadb7 100644 --- a/tests/ui/consts/drop-impl-nonconst-drop-field.stderr +++ b/tests/ui/consts/drop-impl-nonconst-drop-field.stderr @@ -21,6 +21,10 @@ note: required for this `Drop` impl | LL | impl const Drop for ConstDrop2 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider restricting type parameter `T` with unstable trait `Destruct` + | +LL | impl const Drop for ConstDrop2 { + | ++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/consts/trait_alias.fail.stderr b/tests/ui/consts/trait_alias.fail.stderr index 16675206a7a4b..62f5ab1a7d2cd 100644 --- a/tests/ui/consts/trait_alias.fail.stderr +++ b/tests/ui/consts/trait_alias.fail.stderr @@ -3,6 +3,11 @@ error[E0277]: the trait bound `T: [const] Baz` is not satisfied | LL | x.baz(); | ^^^ + | +help: consider further restricting type parameter `T` with trait `Baz` + | +LL | const fn foo(x: &T) { + | +++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/consts/trait_alias.next_fail.stderr b/tests/ui/consts/trait_alias.next_fail.stderr index 16675206a7a4b..62f5ab1a7d2cd 100644 --- a/tests/ui/consts/trait_alias.next_fail.stderr +++ b/tests/ui/consts/trait_alias.next_fail.stderr @@ -3,6 +3,11 @@ error[E0277]: the trait bound `T: [const] Baz` is not satisfied | LL | x.baz(); | ^^^ + | +help: consider further restricting type parameter `T` with trait `Baz` + | +LL | const fn foo(x: &T) { + | +++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/imports/ambiguous-import-visibility-globglob-priv.stderr b/tests/ui/imports/ambiguous-import-visibility-globglob-priv.stderr index 6e77808855a6c..4485f5ac96481 100644 --- a/tests/ui/imports/ambiguous-import-visibility-globglob-priv.stderr +++ b/tests/ui/imports/ambiguous-import-visibility-globglob-priv.stderr @@ -5,10 +5,10 @@ LL | use crate::both::private::S; | ^ private struct import | note: the struct import `S` is defined here... - --> $DIR/ambiguous-import-visibility-globglob-priv.rs:8:24 + --> $DIR/ambiguous-import-visibility-globglob-priv.rs:7:13 | -LL | pub(super) use crate::m::*; - | ^^^^^^^^^^^ +LL | use crate::m::*; + | ^^^^^^^^^^^ note: ...and refers to the struct `S` which is defined here --> $DIR/ambiguous-import-visibility-globglob-priv.rs:2:5 | @@ -27,10 +27,10 @@ LL | use crate::both::private::S; | ^ private struct import | note: the struct import `S` is defined here... - --> $DIR/ambiguous-import-visibility-globglob-priv.rs:8:24 + --> $DIR/ambiguous-import-visibility-globglob-priv.rs:7:13 | -LL | pub(super) use crate::m::*; - | ^^^^^^^^^^^ +LL | use crate::m::*; + | ^^^^^^^^^^^ note: ...and refers to the struct `S` which is defined here --> $DIR/ambiguous-import-visibility-globglob-priv.rs:2:5 | diff --git a/tests/ui/imports/ambiguous-import-visibility-globglob.rs b/tests/ui/imports/ambiguous-import-visibility-globglob.rs index 5af32abd1c600..5c8b6f4766835 100644 --- a/tests/ui/imports/ambiguous-import-visibility-globglob.rs +++ b/tests/ui/imports/ambiguous-import-visibility-globglob.rs @@ -1,7 +1,5 @@ //@ check-pass -// FIXME: should report "ambiguous import visibility" in all the cases below. - mod m { pub struct S {} } @@ -12,7 +10,11 @@ mod min_vis_first { pub use crate::m::*; pub use self::S as S1; + //~^ WARN ambiguous import visibility + //~| WARN this was previously accepted pub(crate) use self::S as S2; + //~^ WARN ambiguous import visibility + //~| WARN this was previously accepted use self::S as S3; // OK } @@ -22,7 +24,11 @@ mod mid_vis_first { pub use crate::m::*; pub use self::S as S1; + //~^ WARN ambiguous import visibility + //~| WARN this was previously accepted pub(crate) use self::S as S2; + //~^ WARN ambiguous import visibility + //~| WARN this was previously accepted use self::S as S3; // OK } @@ -32,7 +38,11 @@ mod max_vis_first { pub(crate) use crate::m::*; pub use self::S as S1; + //~^ WARN ambiguous import visibility + //~| WARN this was previously accepted pub(crate) use self::S as S2; + //~^ WARN ambiguous import visibility + //~| WARN this was previously accepted use self::S as S3; // OK } diff --git a/tests/ui/imports/ambiguous-import-visibility-globglob.stderr b/tests/ui/imports/ambiguous-import-visibility-globglob.stderr new file mode 100644 index 0000000000000..d15282cc436cd --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-globglob.stderr @@ -0,0 +1,135 @@ +warning: ambiguous import visibility: pub or pub(in crate::min_vis_first) + --> $DIR/ambiguous-import-visibility-globglob.rs:12:19 + | +LL | pub use self::S as S1; + | ^ + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:10:13 + | +LL | pub use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:8:9 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: ambiguous import visibility: pub(crate) or pub(in crate::min_vis_first) + --> $DIR/ambiguous-import-visibility-globglob.rs:15:26 + | +LL | pub(crate) use self::S as S2; + | ^ + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:10:13 + | +LL | pub use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:8:9 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + +warning: ambiguous import visibility: pub or pub(in crate::mid_vis_first) + --> $DIR/ambiguous-import-visibility-globglob.rs:26:19 + | +LL | pub use self::S as S1; + | ^ + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:24:13 + | +LL | pub use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:23:9 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + +warning: ambiguous import visibility: pub(crate) or pub(in crate::mid_vis_first) + --> $DIR/ambiguous-import-visibility-globglob.rs:29:26 + | +LL | pub(crate) use self::S as S2; + | ^ + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:24:13 + | +LL | pub use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:23:9 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + +warning: ambiguous import visibility: pub or pub(in crate::max_vis_first) + --> $DIR/ambiguous-import-visibility-globglob.rs:40:19 + | +LL | pub use self::S as S1; + | ^ + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:36:13 + | +LL | pub use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:37:9 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + +warning: ambiguous import visibility: pub(crate) or pub(in crate::max_vis_first) + --> $DIR/ambiguous-import-visibility-globglob.rs:43:26 + | +LL | pub(crate) use self::S as S2; + | ^ + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:36:13 + | +LL | pub use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-globglob.rs:37:9 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + +warning: 6 warnings emitted + diff --git a/tests/ui/imports/overwrite-vis-unused.rs b/tests/ui/imports/overwrite-vis-unused.rs index 0217fb6250837..6361c71f6639e 100644 --- a/tests/ui/imports/overwrite-vis-unused.rs +++ b/tests/ui/imports/overwrite-vis-unused.rs @@ -1,12 +1,12 @@ // Regression test for issues #152004 and #151124. - +//@ check-pass #![deny(unused)] mod m { pub struct S {} } -use m::*; //~ ERROR unused import: `m::*` +use m::*; pub use m::*; fn main() {} diff --git a/tests/ui/imports/overwrite-vis-unused.stderr b/tests/ui/imports/overwrite-vis-unused.stderr deleted file mode 100644 index a38aa4d5f070a..0000000000000 --- a/tests/ui/imports/overwrite-vis-unused.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: unused import: `m::*` - --> $DIR/overwrite-vis-unused.rs:9:5 - | -LL | use m::*; - | ^^^^ - | -note: the lint level is defined here - --> $DIR/overwrite-vis-unused.rs:3:9 - | -LL | #![deny(unused)] - | ^^^^^^ - = note: `#[deny(unused_imports)]` implied by `#[deny(unused)]` - -error: aborting due to 1 previous error - diff --git a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs index dac878c1cd992..ae326193550bc 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs @@ -7,7 +7,6 @@ //@ normalize-stderr: "[^ ]*/foo.dll_imports.lib" -> "$$LIB_FILE" //@ normalize-stderr: "-m [^ ]*" -> "$$TARGET_MACHINE" //@ normalize-stderr: "-f [^ ]*" -> "$$ASM_FLAGS" -//@ normalize-stderr: "--temp-prefix [^ ]*/foo.dll" -> "$$TEMP_PREFIX" #[link(name = "foo", kind = "raw-dylib")] extern "C" { // `@1` is an invalid name to export, as it usually indicates that something diff --git a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr index 6fcb07cf3882c..b7279f23e6676 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr @@ -1,4 +1,4 @@ -error: dlltool could not create import library with $DLLTOOL -d $DEF_FILE -D foo.dll -l $LIB_FILE $TARGET_MACHINE $ASM_FLAGS --no-leading-underscore $TEMP_PREFIX: +error: dlltool could not create import library with $DLLTOOL -d $DEF_FILE -D foo.dll -l $LIB_FILE $TARGET_MACHINE $ASM_FLAGS --no-leading-underscore --temp-prefix foo.dll: $DLLTOOL: Syntax error in def file $DEF_FILE:1␍ diff --git a/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr index 86a53a030f50c..01d081cc3041c 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr @@ -2,9 +2,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/import-name-type-invalid-format.rs:9:1 | LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = 6)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------------------^^ - | | - | expected this to be of the form `import_name_type = "..."` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ + | | + | expected a string literal here | = note: for more information, visit diff --git a/tests/ui/simd/intrinsic/generic-reduction-pass.rs b/tests/ui/simd/intrinsic/generic-reduction-pass.rs index 2c615cd729e7b..91edb0c5add62 100644 --- a/tests/ui/simd/intrinsic/generic-reduction-pass.rs +++ b/tests/ui/simd/intrinsic/generic-reduction-pass.rs @@ -109,10 +109,11 @@ const fn ordered() { let r: f32 = simd_reduce_mul_ordered(x, 2.); assert_eq!(r, -48_f32); - let r: f32 = simd_reduce_min(x); - assert_eq!(r, -2_f32); - let r: f32 = simd_reduce_max(x); - assert_eq!(r, 4_f32); + // FIXME: re-enable when the intrinsic works on floats again. + // let r: f32 = simd_reduce_min(x); + // assert_eq!(r, -2_f32); + // let r: f32 = simd_reduce_max(x); + // assert_eq!(r, 4_f32); } unsafe { diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 1022929118bd6..0c380d69b1433 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -31,7 +31,7 @@ extern "Rust" {} //~| NOTE expected this to be of the form `enable = "..."` #[target_feature(disable = "baz")] //~^ ERROR malformed `target_feature` attribute -//~| NOTE expected this to be of the form `enable = "..."` +//~| NOTE the only valid argument here is `enable` unsafe fn foo() {} #[target_feature(enable = "sse2")] diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 64d4ceff5a87b..6de12edb037fe 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -56,7 +56,7 @@ error[E0539]: malformed `target_feature` attribute input LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ | | - | expected this to be of the form `enable = "..."` + | the only valid argument here is `enable` | help: must be of the form | diff --git a/tests/ui/test-attrs/test-should-panic-attr.rs b/tests/ui/test-attrs/test-should-panic-attr.rs index b095099daad0f..8207c4c0be753 100644 --- a/tests/ui/test-attrs/test-should-panic-attr.rs +++ b/tests/ui/test-attrs/test-should-panic-attr.rs @@ -19,7 +19,7 @@ fn test2() { #[test] #[should_panic(expect)] //~^ ERROR malformed `should_panic` attribute input -//~| NOTE the only valid argument here is "expected" +//~| NOTE expected this to be of the form `expected = "..."` //~| NOTE for more information, visit fn test3() { panic!(); diff --git a/tests/ui/test-attrs/test-should-panic-attr.stderr b/tests/ui/test-attrs/test-should-panic-attr.stderr index 48f6b0d37cb10..327444d9885c8 100644 --- a/tests/ui/test-attrs/test-should-panic-attr.stderr +++ b/tests/ui/test-attrs/test-should-panic-attr.stderr @@ -22,9 +22,9 @@ error[E0539]: malformed `should_panic` attribute input --> $DIR/test-should-panic-attr.rs:20:1 | LL | #[should_panic(expect)] - | ^^^^^^^^^^^^^^--------^ - | | - | the only valid argument here is "expected" + | ^^^^^^^^^^^^^^^------^^ + | | + | expected this to be of the form `expected = "..."` | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute diff --git a/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr b/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr index 65c6f833ccea1..3e93ce8cbd7a6 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr @@ -15,6 +15,11 @@ error[E0277]: the trait bound `T: [const] Foo` is not satisfied | LL | x.a(); | ^ + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | const fn foo(x: &T) { + | +++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr b/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr index c17a67132116e..ea637e1ab8db8 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr @@ -3,6 +3,11 @@ error[E0277]: the trait bound `T: [const] Foo` is not satisfied | LL | x.a(); | ^ + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | const fn foo(x: &T) { + | +++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.yyn.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.yyn.stderr index 7b5a1f7a6a230..6fd236a0de4fe 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-3.yyn.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-3.yyn.stderr @@ -38,6 +38,11 @@ error[E0277]: the trait bound `T: [const] Foo` is not satisfied | LL | x.a(); | ^ + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | const fn foo(x: &T) { + | +++++++++++++ error: aborting due to 4 previous errors