From f847388f6af63ce43b4d1a0f510a357e95eb64d7 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Tue, 12 Jul 2022 13:52:35 -0700 Subject: [PATCH 01/12] Implementation of import_name_type --- .../rustc_codegen_llvm/src/back/archive.rs | 34 +++----- compiler/rustc_codegen_llvm/src/callee.rs | 12 ++- compiler/rustc_codegen_llvm/src/common.rs | 76 ++++++++++++++++++ compiler/rustc_codegen_llvm/src/consts.rs | 12 ++- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- compiler/rustc_metadata/src/native_libs.rs | 79 +++++++++++++++++-- .../src/rmeta/decoder/cstore_impl.rs | 4 +- compiler/rustc_middle/src/query/mod.rs | 3 + compiler/rustc_session/src/cstore.rs | 33 +++++++- compiler/rustc_span/src/symbol.rs | 1 + .../raw-dylib-import-name-type/Makefile | 22 ++++++ .../raw-dylib-import-name-type/driver.rs | 79 +++++++++++++++++++ .../raw-dylib-import-name-type/extern.c | 65 +++++++++++++++ .../raw-dylib-import-name-type/extern.gnu.def | 18 +++++ .../extern.msvc.def | 18 +++++ .../raw-dylib-import-name-type/output.txt | 12 +++ ...feature-gate-raw-dylib-import-name-type.rs | 8 ++ ...ure-gate-raw-dylib-import-name-type.stderr | 21 +++++ .../link-attr-validation-early.stderr | 4 +- .../link-attr-validation-late.stderr | 4 +- .../ui/malformed/malformed-regressions.stderr | 4 +- .../import-name-type-invalid-format.rs | 10 +++ .../import-name-type-invalid-format.stderr | 17 ++++ .../import-name-type-multiple.rs | 11 +++ .../import-name-type-multiple.stderr | 17 ++++ .../import-name-type-unknown-value.rs | 10 +++ .../import-name-type-unknown-value.stderr | 17 ++++ .../import-name-type-unsupported-link-kind.rs | 18 +++++ ...ort-name-type-unsupported-link-kind.stderr | 23 ++++++ .../import-name-type-x86-only.rs | 9 +++ .../import-name-type-x86-only.stderr | 17 ++++ 31 files changed, 614 insertions(+), 46 deletions(-) create mode 100644 src/test/run-make/raw-dylib-import-name-type/Makefile create mode 100644 src/test/run-make/raw-dylib-import-name-type/driver.rs create mode 100644 src/test/run-make/raw-dylib-import-name-type/extern.c create mode 100644 src/test/run-make/raw-dylib-import-name-type/extern.gnu.def create mode 100644 src/test/run-make/raw-dylib-import-name-type/extern.msvc.def create mode 100644 src/test/run-make/raw-dylib-import-name-type/output.txt create mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.rs create mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.stderr create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs create mode 100644 src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 27039cda25382..2e614e5dd88e7 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -8,10 +8,11 @@ use std::path::{Path, PathBuf}; use std::ptr; use std::str; +use crate::common; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; -use rustc_session::cstore::{DllCallingConvention, DllImport}; +use rustc_session::cstore::DllImport; use rustc_session::Session; /// Helper for adding many files to an archive. @@ -111,21 +112,18 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { }; let target = &sess.target; - let mingw_gnu_toolchain = target.vendor == "pc" - && target.os == "windows" - && target.env == "gnu" - && target.abi.is_empty(); + let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target); let import_name_and_ordinal_vector: Vec<(String, Option)> = dll_imports .iter() .map(|import: &DllImport| { if sess.target.arch == "x86" { ( - LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), - import.ordinal, + common::i686_decorated_name(import, mingw_gnu_toolchain, false), + import.ordinal(), ) } else { - (import.name.to_string(), import.ordinal) + (import.name.to_string(), import.ordinal()) } }) .collect(); @@ -159,6 +157,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { } }; + // --no-leading-underscore: For the `import_name_type` feature to work, we need to be + // 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 result = std::process::Command::new(dlltool) .args([ @@ -168,6 +169,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { lib_name, "-l", output_path.to_str().unwrap(), + "--no-leading-underscore", ]) .output(); @@ -322,22 +324,6 @@ impl<'a> LlvmArchiveBuilder<'a> { ret } } - - fn i686_decorated_name(import: &DllImport, mingw: bool) -> String { - let name = import.name; - let prefix = if mingw { "" } else { "_" }; - - match import.calling_convention { - DllCallingConvention::C => format!("{}{}", prefix, name), - DllCallingConvention::Stdcall(arg_list_size) => { - format!("{}{}@{}", prefix, name, arg_list_size) - } - DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size), - DllCallingConvention::Vectorcall(arg_list_size) => { - format!("{}@@{}", name, arg_list_size) - } - } - } } fn string_to_io_error(s: String) -> io::Error { diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 72155d874a257..d55f995b933a6 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -6,6 +6,7 @@ use crate::abi::FnAbiLlvmExt; use crate::attributes; +use crate::common; use crate::context::CodegenCx; use crate::llvm; use crate::value::Value; @@ -79,13 +80,18 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> llfn } } else { - let llfn = cx.declare_fn(sym, fn_abi); + let instance_def_id = instance.def_id(); + let llfn = if tcx.sess.target.arch == "x86" && + let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym) + { + cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi) + } else { + cx.declare_fn(sym, fn_abi) + }; debug!("get_fn: not casting pointer!"); attributes::from_fn_attrs(cx, llfn, instance); - let instance_def_id = instance.def_id(); - // Apply an appropriate linkage/visibility value to our item that we // just declared. // diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index fb4da9a5f3370..63d3bb40a3fe0 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -10,12 +10,17 @@ use crate::value::Value; use rustc_ast::Mutability; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; +use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::TyCtxt; +use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer, Size}; +use rustc_target::spec::Target; use libc::{c_char, c_uint}; +use std::fmt::Write; use tracing::debug; /* @@ -357,3 +362,74 @@ fn hi_lo_to_u128(lo: u64, hi: u64) -> u128 { fn try_as_const_integral(v: &Value) -> Option<&ConstantInt> { unsafe { llvm::LLVMIsAConstantInt(v) } } + +pub(crate) fn get_dllimport<'tcx>( + tcx: TyCtxt<'tcx>, + id: DefId, + name: &str, +) -> Option<&'tcx DllImport> { + tcx.native_library(id) + .map(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name)) + .flatten() +} + +pub(crate) fn is_mingw_gnu_toolchain(target: &Target) -> bool { + target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() +} + +pub(crate) fn i686_decorated_name( + dll_import: &DllImport, + mingw: bool, + disable_name_mangling: bool, +) -> String { + let name = dll_import.name.as_str(); + + let (add_prefix, add_suffix) = match dll_import.import_name_type { + Some(PeImportNameType::NoPrefix) => (false, true), + Some(PeImportNameType::Undecorated) => (false, false), + _ => (true, true), + }; + + // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). + let mut decorated_name = String::with_capacity(name.len() + 6); + + if disable_name_mangling { + // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. + decorated_name.push('\x01'); + } + + let prefix = if add_prefix && dll_import.is_fn { + match dll_import.calling_convention { + DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, + DllCallingConvention::Stdcall(_) => (!mingw + || dll_import.import_name_type == Some(PeImportNameType::Decorated)) + .then_some('_'), + DllCallingConvention::Fastcall(_) => Some('@'), + } + } else if !dll_import.is_fn && !mingw { + // For static variables, prefix with '_' on MSVC. + Some('_') + } else { + None + }; + if let Some(prefix) = prefix { + decorated_name.push(prefix); + } + + decorated_name.push_str(name); + + if add_suffix && dll_import.is_fn { + match dll_import.calling_convention { + DllCallingConvention::C => {} + DllCallingConvention::Stdcall(arg_list_size) + | DllCallingConvention::Fastcall(arg_list_size) => { + write!(&mut decorated_name, "@{}", arg_list_size).unwrap(); + } + DllCallingConvention::Vectorcall(arg_list_size) => { + write!(&mut decorated_name, "@@{}", arg_list_size).unwrap(); + } + } + } + + decorated_name +} diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 18467e37082d6..f41ff325590af 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,5 +1,5 @@ use crate::base; -use crate::common::CodegenCx; +use crate::common::{self, CodegenCx}; use crate::debuginfo; use crate::llvm::{self, True}; use crate::llvm_util; @@ -160,7 +160,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, - span_def_id: DefId, + def_id: DefId, ) -> &'ll Value { let llty = cx.layout_of(ty).llvm_type(cx); if let Some(linkage) = attrs.linkage { @@ -175,7 +175,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( cx.layout_of(mt.ty).llvm_type(cx) } else { cx.sess().span_fatal( - cx.tcx.def_span(span_def_id), + cx.tcx.def_span(def_id), "must have type `*const T` or `*mut T` due to `#[linkage]` attribute", ) }; @@ -194,7 +194,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( real_name.push_str(sym); let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| { cx.sess().span_fatal( - cx.tcx.def_span(span_def_id), + cx.tcx.def_span(def_id), &format!("symbol `{}` is already defined", &sym), ) }); @@ -202,6 +202,10 @@ fn check_and_apply_linkage<'ll, 'tcx>( llvm::LLVMSetInitializer(g2, g1); g2 } + } else if cx.tcx.sess.target.arch == "x86" && + let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym) + { + cx.declare_global(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&cx.tcx.sess.target), true), llty) } else { // Generate an external declaration. // FIXME(nagisa): investigate whether it can be changed into define_global diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7d3bedbfe4319..d520efed9b8b8 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -335,7 +335,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ABI, linking, symbols, and FFI ungated!( link, Normal, - template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), + template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#), DuplicatesOk, ), ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index c5e39507d7e4d..8bafe203748f3 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -5,7 +5,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; -use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib}; +use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib, PeImportNameType}; use rustc_session::parse::feature_err; use rustc_session::utils::NativeLibKind; use rustc_session::Session; @@ -61,6 +61,7 @@ impl<'tcx> Collector<'tcx> { let mut modifiers = None; let mut cfg = None; let mut wasm_import_module = None; + let mut import_name_type = None; for item in items.iter() { match item.name_or_empty() { sym::name => { @@ -199,9 +200,51 @@ impl<'tcx> Collector<'tcx> { }; wasm_import_module = Some((link_wasm_import_module, item.span())); } + sym::import_name_type => { + if import_name_type.is_some() { + let msg = "multiple `import_name_type` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_import_name_type) = item.value_str() else { + let msg = "import name type must be of the form `import_name_type = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + if self.tcx.sess.target.arch != "x86" { + let msg = "import name type is only supported on x86"; + sess.span_err(item.span(), msg); + continue; + } + + let link_import_name_type = match link_import_name_type.as_str() { + "decorated" => PeImportNameType::Decorated, + "noprefix" => PeImportNameType::NoPrefix, + "undecorated" => PeImportNameType::Undecorated, + import_name_type => { + let msg = format!( + "unknown import name type `{import_name_type}`, expected one of: \ + decorated, noprefix, undecorated" + ); + sess.span_err(item.span(), msg); + continue; + } + }; + if !features.raw_dylib { + let span = item.name_value_literal_span().unwrap(); + feature_err( + &sess.parse_sess, + sym::raw_dylib, + span, + "import name type is unstable", + ) + .emit(); + } + import_name_type = Some((link_import_name_type, item.span())); + } _ => { let msg = "unexpected `#[link]` argument, expected one of: \ - name, kind, modifiers, cfg, wasm_import_module"; + name, kind, modifiers, cfg, wasm_import_module, import_name_type"; sess.span_err(item.span(), msg); } } @@ -315,6 +358,14 @@ impl<'tcx> Collector<'tcx> { .emit(); } + // Do this outside of the loop so that `import_name_type` can be specified before `kind`. + if let Some((_, span)) = import_name_type { + if kind != Some(NativeLibKind::RawDylib) { + let msg = "import name type can only be used with link kind `raw-dylib`"; + sess.span_err(span, msg); + } + } + let dll_imports = match kind { Some(NativeLibKind::RawDylib) => { if let Some((name, span)) = name && name.as_str().contains('\0') { @@ -325,7 +376,13 @@ impl<'tcx> Collector<'tcx> { } foreign_mod_items .iter() - .map(|child_item| self.build_dll_import(abi, child_item)) + .map(|child_item| { + self.build_dll_import( + abi, + import_name_type.map(|(import_name_type, _)| import_name_type), + child_item, + ) + }) .collect() } _ => { @@ -486,7 +543,12 @@ impl<'tcx> Collector<'tcx> { .sum() } - fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport { + fn build_dll_import( + &self, + abi: Abi, + import_name_type: Option, + item: &hir::ForeignItemRef, + ) -> DllImport { let calling_convention = if self.tcx.sess.target.arch == "x86" { match abi { Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C, @@ -518,11 +580,18 @@ impl<'tcx> Collector<'tcx> { } }; + let import_name_type = self + .tcx + .codegen_fn_attrs(item.id.def_id) + .link_ordinal + .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord))); + DllImport { name: item.ident.name, - ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal, + import_name_type, calling_convention, span: item.span, + is_fn: self.tcx.def_kind(item.id.def_id).is_fn_like(), } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index ad2b6846a4bc0..11ab20ee1415d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -341,7 +341,8 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { assert_eq!(cnum, LOCAL_CRATE); false }, - native_library_kind: |tcx, id| { + native_library_kind: |tcx, id| tcx.native_library(id).map(|l| l.kind), + native_library: |tcx, id| { tcx.native_libraries(id.krate) .iter() .filter(|lib| native_libs::relevant_lib(&tcx.sess, lib)) @@ -355,7 +356,6 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { .foreign_items .contains(&id) }) - .map(|l| l.kind) }, native_libraries: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f78f62e31d286..6e315b7ae494e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1565,6 +1565,9 @@ rustc_queries! { -> Option { desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } } + query native_library(def_id: DefId) -> Option<&'tcx NativeLib> { + desc { |tcx| "native_library({})", tcx.def_path_str(def_id) } + } /// Does lifetime resolution, but does not descend into trait items. This /// should only be used for resolving lifetimes of on trait definitions, diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index c1fd3c7c61b9e..5d617245e0f98 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -81,10 +81,29 @@ impl NativeLib { } } +/// Different ways that the PE Format can decorate a symbol name. +/// From https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-name-type +#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, PartialEq, Eq)] +pub enum PeImportNameType { + /// IMPORT_ORDINAL + /// Uses the ordinal (i.e., a number) rather than the name. + Ordinal(u16), + /// Same as IMPORT_NAME + /// Name is decorated with all prefixes and suffixes. + Decorated, + /// Same as IMPORT_NAME_NOPREFIX + /// Prefix (e.g., the leading `_` or `@`) is skipped, but suffix is kept. + NoPrefix, + /// Same as IMPORT_NAME_UNDECORATE + /// Prefix (e.g., the leading `_` or `@`) and suffix (the first `@` and all + /// trailing characters) are skipped. + Undecorated, +} + #[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] pub struct DllImport { pub name: Symbol, - pub ordinal: Option, + pub import_name_type: Option, /// Calling convention for the function. /// /// On x86_64, this is always `DllCallingConvention::C`; on i686, it can be any @@ -92,6 +111,18 @@ pub struct DllImport { pub calling_convention: DllCallingConvention, /// Span of import's "extern" declaration; used for diagnostics. pub span: Span, + /// Is this for a function (rather than a static variable). + pub is_fn: bool, +} + +impl DllImport { + pub fn ordinal(&self) -> Option { + if let Some(PeImportNameType::Ordinal(ordinal)) = self.import_name_type { + Some(ordinal) + } else { + None + } + } } /// Calling convention for a function defined in an external library. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 156f53ac48626..77b0b5983d196 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -802,6 +802,7 @@ symbols! { impl_trait_in_bindings, implied_by, import, + import_name_type, import_shadowing, imported_main, in_band_lifetimes, diff --git a/src/test/run-make/raw-dylib-import-name-type/Makefile b/src/test/run-make/raw-dylib-import-name-type/Makefile new file mode 100644 index 0000000000000..fcc60e88e1a43 --- /dev/null +++ b/src/test/run-make/raw-dylib-import-name-type/Makefile @@ -0,0 +1,22 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions. + +# only-x86 +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + $(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/extern.obj extern.msvc.def -link -dll -out:"$(TMPDIR)"/extern.dll -noimplib +else + $(CC) "$(TMPDIR)"/extern.obj extern.gnu.def --no-leading-underscore -shared -o "$(TMPDIR)"/extern.dll +endif + "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.txt output.txt +else + $(DIFF) output.txt "$(TMPDIR)"/output.txt +endif diff --git a/src/test/run-make/raw-dylib-import-name-type/driver.rs b/src/test/run-make/raw-dylib-import-name-type/driver.rs new file mode 100644 index 0000000000000..74e9a89fbdf32 --- /dev/null +++ b/src/test/run-make/raw-dylib-import-name-type/driver.rs @@ -0,0 +1,79 @@ +#![feature(raw_dylib)] + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] +extern "C" { + fn cdecl_fn_undecorated(i: i32); + static mut extern_variable_undecorated: i32; +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "noprefix")] +extern "C" { + fn cdecl_fn_noprefix(i: i32); + static mut extern_variable_noprefix: i32; +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "decorated")] +extern "C" { + fn cdecl_fn_decorated(i: i32); + static mut extern_variable_decorated: i32; +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] +extern "stdcall" { + fn stdcall_fn_undecorated(i: i32); +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "noprefix")] +extern "stdcall" { + fn stdcall_fn_noprefix(i: i32); +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "decorated")] +extern "stdcall" { + fn stdcall_fn_decorated(i: i32); +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] +extern "fastcall" { + fn fastcall_fn_undecorated(i: i32); +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "noprefix")] +extern "fastcall" { + fn fastcall_fn_noprefix(i: i32); +} + +#[link(name = "extern", kind = "raw-dylib", import_name_type = "decorated")] +extern "fastcall" { + fn fastcall_fn_decorated(i: i32); +} + +#[link(name = "extern", kind = "raw-dylib")] +extern { + fn print_extern_variable_undecorated(); + fn print_extern_variable_noprefix(); + fn print_extern_variable_decorated(); +} + +pub fn main() { + unsafe { + cdecl_fn_undecorated(1); + cdecl_fn_noprefix(2); + cdecl_fn_decorated(3); + + stdcall_fn_undecorated(4); + stdcall_fn_noprefix(5); + stdcall_fn_decorated(6); + + fastcall_fn_undecorated(7); + fastcall_fn_noprefix(8); + fastcall_fn_decorated(9); + + extern_variable_undecorated = 42; + print_extern_variable_undecorated(); + extern_variable_noprefix = 43; + print_extern_variable_noprefix(); + extern_variable_decorated = 44; + print_extern_variable_decorated(); + } +} diff --git a/src/test/run-make/raw-dylib-import-name-type/extern.c b/src/test/run-make/raw-dylib-import-name-type/extern.c new file mode 100644 index 0000000000000..1102158e2494e --- /dev/null +++ b/src/test/run-make/raw-dylib-import-name-type/extern.c @@ -0,0 +1,65 @@ +#include +#include + +void _cdecl cdecl_fn_undecorated(int i) { + printf("cdecl_fn_undecorated(%d)\n", i); + fflush(stdout); +} + +void _cdecl cdecl_fn_noprefix(int i) { + printf("cdecl_fn_noprefix(%d)\n", i); + fflush(stdout); +} + +void _cdecl cdecl_fn_decorated(int i) { + printf("cdecl_fn_decorated(%d)\n", i); + fflush(stdout); +} + +void __stdcall stdcall_fn_undecorated(int i) { + printf("stdcall_fn_undecorated(%d)\n", i); + fflush(stdout); +} + +void __stdcall stdcall_fn_noprefix(int i) { + printf("stdcall_fn_noprefix(%d)\n", i); + fflush(stdout); +} + +void __stdcall stdcall_fn_decorated(int i) { + printf("stdcall_fn_decorated(%d)\n", i); + fflush(stdout); +} + +void __fastcall fastcall_fn_undecorated(int i) { + printf("fastcall_fn_undecorated(%d)\n", i); + fflush(stdout); +} + +void __fastcall fastcall_fn_noprefix(int i) { + printf("fastcall_fn_noprefix(%d)\n", i); + fflush(stdout); +} + +void __fastcall fastcall_fn_decorated(int i) { + printf("fastcall_fn_decorated(%d)\n", i); + fflush(stdout); +} + +int extern_variable_undecorated = 0; +__declspec(dllexport) void print_extern_variable_undecorated() { + printf("extern_variable_undecorated value: %d\n", extern_variable_undecorated); + fflush(stdout); +} + +int extern_variable_noprefix = 0; +__declspec(dllexport) void print_extern_variable_noprefix() { + printf("extern_variable_noprefix value: %d\n", extern_variable_noprefix); + fflush(stdout); +} + +int extern_variable_decorated = 0; +__declspec(dllexport) void print_extern_variable_decorated() { + printf("extern_variable_decorated value: %d\n", extern_variable_decorated); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def b/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def new file mode 100644 index 0000000000000..f06ce67e0308b --- /dev/null +++ b/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def @@ -0,0 +1,18 @@ +LIBRARY extern +EXPORTS + cdecl_fn_undecorated + cdecl_fn_noprefix + cdecl_fn_decorated + stdcall_fn_undecorated + stdcall_fn_noprefix@4 + fastcall_fn_undecorated + @fastcall_fn_decorated@4 + + ;ld doesn't handle fully-decorated stdcall, or no-prefix fastcall + _stdcall_fn_decorated@4=stdcall_fn_decorated@4 + fastcall_fn_noprefix@4=@fastcall_fn_noprefix@4 + + ;Variables are never decorated + extern_variable_undecorated + extern_variable_noprefix + extern_variable_decorated diff --git a/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def b/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def new file mode 100644 index 0000000000000..9dc333707cb0a --- /dev/null +++ b/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def @@ -0,0 +1,18 @@ +LIBRARY extern +EXPORTS + cdecl_fn_undecorated + cdecl_fn_noprefix + cdecl_fn_decorated + stdcall_fn_undecorated + _stdcall_fn_decorated@4 + fastcall_fn_undecorated + @fastcall_fn_decorated@4 + + ;MSVC doesn't seem to recognize the "no prefix" syntax. + stdcall_fn_noprefix@4=_stdcall_fn_noprefix@4 + fastcall_fn_noprefix@4=@fastcall_fn_noprefix@4 + + ;Variables are never decorated + extern_variable_undecorated + extern_variable_noprefix + extern_variable_decorated diff --git a/src/test/run-make/raw-dylib-import-name-type/output.txt b/src/test/run-make/raw-dylib-import-name-type/output.txt new file mode 100644 index 0000000000000..855b20a864575 --- /dev/null +++ b/src/test/run-make/raw-dylib-import-name-type/output.txt @@ -0,0 +1,12 @@ +cdecl_fn_undecorated(1) +cdecl_fn_noprefix(2) +cdecl_fn_decorated(3) +stdcall_fn_undecorated(4) +stdcall_fn_noprefix(5) +stdcall_fn_decorated(6) +fastcall_fn_undecorated(7) +fastcall_fn_noprefix(8) +fastcall_fn_decorated(9) +extern_variable_undecorated value: 42 +extern_variable_noprefix value: 43 +extern_variable_decorated value: 44 diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.rs new file mode 100644 index 0000000000000..33655cf8bdc7e --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.rs @@ -0,0 +1,8 @@ +// only-windows +// only-x86 +#[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated")] +//~^ ERROR link kind `raw-dylib` is unstable +//~| ERROR import name type is unstable +extern "C" {} + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.stderr new file mode 100644 index 0000000000000..be82dd119264f --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-import-name-type.stderr @@ -0,0 +1,21 @@ +error[E0658]: link kind `raw-dylib` is unstable + --> $DIR/feature-gate-raw-dylib-import-name-type.rs:3:29 + | +LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated")] + | ^^^^^^^^^^^ + | + = note: see issue #58713 for more information + = help: add `#![feature(raw_dylib)]` to the crate attributes to enable + +error[E0658]: import name type is unstable + --> $DIR/feature-gate-raw-dylib-import-name-type.rs:3:61 + | +LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated")] + | ^^^^^^^^^^^ + | + = note: see issue #58713 for more information + = help: add `#![feature(raw_dylib)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/linkage-attr/link-attr-validation-early.stderr b/src/test/ui/linkage-attr/link-attr-validation-early.stderr index d36601ed0b49b..903141e439db3 100644 --- a/src/test/ui/linkage-attr/link-attr-validation-early.stderr +++ b/src/test/ui/linkage-attr/link-attr-validation-early.stderr @@ -1,4 +1,4 @@ -error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]` +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]` --> $DIR/link-attr-validation-early.rs:2:1 | LL | #[link] @@ -8,7 +8,7 @@ LL | #[link] = 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 #57571 -error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]` +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]` --> $DIR/link-attr-validation-early.rs:4:1 | LL | #[link = "foo"] diff --git a/src/test/ui/linkage-attr/link-attr-validation-late.stderr b/src/test/ui/linkage-attr/link-attr-validation-late.stderr index bb08f9a4c029f..dd0f1dba2ecf7 100644 --- a/src/test/ui/linkage-attr/link-attr-validation-late.stderr +++ b/src/test/ui/linkage-attr/link-attr-validation-late.stderr @@ -1,10 +1,10 @@ -error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module +error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module, import_name_type --> $DIR/link-attr-validation-late.rs:5:22 | LL | #[link(name = "...", "literal")] | ^^^^^^^^^ -error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module +error: unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module, import_name_type --> $DIR/link-attr-validation-late.rs:6:22 | LL | #[link(name = "...", unknown)] diff --git a/src/test/ui/malformed/malformed-regressions.stderr b/src/test/ui/malformed/malformed-regressions.stderr index 13c12ff721360..8c2625bdf0d2c 100644 --- a/src/test/ui/malformed/malformed-regressions.stderr +++ b/src/test/ui/malformed/malformed-regressions.stderr @@ -26,7 +26,7 @@ LL | #[inline = ""] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 -error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]` +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]` --> $DIR/malformed-regressions.rs:7:1 | LL | #[link] @@ -35,7 +35,7 @@ LL | #[link] = 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 #57571 -error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]` +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]` --> $DIR/malformed-regressions.rs:9:1 | LL | #[link = ""] diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs b/src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs new file mode 100644 index 0000000000000..9e739c02dc8e7 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs @@ -0,0 +1,10 @@ +// only-windows +// only-x86 +#![feature(raw_dylib)] +//~^ WARN the feature `raw_dylib` is incomplete + +#[link(name = "foo", kind = "raw-dylib", import_name_type = 6)] +//~^ ERROR import name type must be of the form `import_name_type = "string"` +extern "C" { } + +fn main() {} diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr b/src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr new file mode 100644 index 0000000000000..ee10b0114a478 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr @@ -0,0 +1,17 @@ +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/import-name-type-invalid-format.rs:3:12 + | +LL | #![feature(raw_dylib)] + | ^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information + +error: import name type must be of the form `import_name_type = "string"` + --> $DIR/import-name-type-invalid-format.rs:6:42 + | +LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = 6)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs b/src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs new file mode 100644 index 0000000000000..c404dbae46866 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs @@ -0,0 +1,11 @@ +// ignore-tidy-linelength +// only-windows +// only-x86 +#![feature(raw_dylib)] +//~^ WARN the feature `raw_dylib` is incomplete + +#[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated", import_name_type = "decorated")] +//~^ ERROR multiple `import_name_type` arguments in a single `#[link]` attribute +extern "C" { } + +fn main() {} diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr b/src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr new file mode 100644 index 0000000000000..936c8aa735967 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr @@ -0,0 +1,17 @@ +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/import-name-type-multiple.rs:4:12 + | +LL | #![feature(raw_dylib)] + | ^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information + +error: multiple `import_name_type` arguments in a single `#[link]` attribute + --> $DIR/import-name-type-multiple.rs:7:74 + | +LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated", import_name_type = "decorated")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs new file mode 100644 index 0000000000000..350b0294641ab --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs @@ -0,0 +1,10 @@ +// only-windows +// only-x86 +#![feature(raw_dylib)] +//~^ WARN the feature `raw_dylib` is incomplete + +#[link(name = "foo", kind = "raw-dylib", import_name_type = "unknown")] +//~^ ERROR unknown import name type `unknown`, expected one of: decorated, noprefix, undecorated +extern "C" { } + +fn main() {} diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr new file mode 100644 index 0000000000000..b6871a0d31702 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr @@ -0,0 +1,17 @@ +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/import-name-type-unknown-value.rs:3:12 + | +LL | #![feature(raw_dylib)] + | ^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information + +error: unknown import name type `unknown`, expected one of: decorated, noprefix, undecorated + --> $DIR/import-name-type-unknown-value.rs:6:42 + | +LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = "unknown")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs new file mode 100644 index 0000000000000..b467917bacc3a --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs @@ -0,0 +1,18 @@ +// only-windows +// only-x86 +#![feature(raw_dylib)] +//~^ WARN the feature `raw_dylib` is incomplete + +#[link(name = "foo", import_name_type = "decorated")] +//~^ ERROR import name type can only be used with link kind `raw-dylib` +extern "C" { } + +#[link(name = "bar", kind = "static", import_name_type = "decorated")] +//~^ ERROR import name type can only be used with link kind `raw-dylib` +extern "C" { } + +// Specifying `import_name_type` before `kind` shouldn't raise an error. +#[link(name = "bar", import_name_type = "decorated", kind = "raw-dylib")] +extern "C" { } + +fn main() {} diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr new file mode 100644 index 0000000000000..c35333fb88df8 --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr @@ -0,0 +1,23 @@ +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/import-name-type-unsupported-link-kind.rs:3:12 + | +LL | #![feature(raw_dylib)] + | ^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information + +error: import name type can only be used with link kind `raw-dylib` + --> $DIR/import-name-type-unsupported-link-kind.rs:6:22 + | +LL | #[link(name = "foo", import_name_type = "decorated")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: import name type can only be used with link kind `raw-dylib` + --> $DIR/import-name-type-unsupported-link-kind.rs:10:39 + | +LL | #[link(name = "bar", kind = "static", import_name_type = "decorated")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs b/src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs new file mode 100644 index 0000000000000..4e6de7d6ac37c --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs @@ -0,0 +1,9 @@ +// only-windows +// ignore-x86 +#![feature(raw_dylib)] +//~^ WARN the feature `raw_dylib` is incomplete +#[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated")] +//~^ ERROR import name type is only supported on x86 +extern "C" { } + +fn main() {} diff --git a/src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr b/src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr new file mode 100644 index 0000000000000..d8a585145a73e --- /dev/null +++ b/src/test/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr @@ -0,0 +1,17 @@ +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/import-name-type-x86-only.rs:3:12 + | +LL | #![feature(raw_dylib)] + | ^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information + +error: import name type is only supported on x86 + --> $DIR/import-name-type-x86-only.rs:5:42 + | +LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = "decorated")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + From 69715c903312c3e734bfd9098a5e8773f2e19d01 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sat, 20 Aug 2022 20:47:31 +0000 Subject: [PATCH 02/12] sugg: suggest the usage of boolean value when there is a typo in the keyword Signed-off-by: Vincenzo Palazzo --- .../rustc_resolve/src/late/diagnostics.rs | 27 +++++++++++++++---- .../ui/suggestions/bool_typo_err_suggest.rs | 12 +++++++++ .../suggestions/bool_typo_err_suggest.stderr | 25 +++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/suggestions/bool_typo_err_suggest.rs create mode 100644 src/test/ui/suggestions/bool_typo_err_suggest.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4175527da470c..9bb6e4645804b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -250,13 +250,30 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .map_or_else(String::new, |res| format!("{} ", res.descr())); (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None) }; + + let (fallback_label, suggestion) = if path_str == "async" + && expected.starts_with("struct") + { + ("`async` blocks are only allowed in Rust 2018 or later".to_string(), suggestion) + } else { + // check if we are in situation of typo like `True` instead of `true`. + let override_suggestion = + if ["true", "false"].contains(&item_str.to_string().to_lowercase().as_str()) { + let item_typo = item_str.to_string().to_lowercase(); + Some(( + item_span, + "you may want to use a bool value instead", + format!("{}", item_typo), + )) + } else { + suggestion + }; + (format!("not found in {mod_str}"), override_suggestion) + }; + BaseError { msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"), - fallback_label: if path_str == "async" && expected.starts_with("struct") { - "`async` blocks are only allowed in Rust 2018 or later".to_string() - } else { - format!("not found in {mod_str}") - }, + fallback_label, span: item_span, span_label: None, could_be_expr: false, diff --git a/src/test/ui/suggestions/bool_typo_err_suggest.rs b/src/test/ui/suggestions/bool_typo_err_suggest.rs new file mode 100644 index 0000000000000..deab0fb05b76b --- /dev/null +++ b/src/test/ui/suggestions/bool_typo_err_suggest.rs @@ -0,0 +1,12 @@ +// Suggest the boolean value instead of emit a generic error that the value +// True is not in the scope. + +fn main() { + let x = True; + //~^ ERROR cannot find value `True` in this scope + //~| HELP you may want to use a bool value instead + + let y = False; + //~^ ERROR cannot find value `False` in this scope + //~| HELP you may want to use a bool value instead +} diff --git a/src/test/ui/suggestions/bool_typo_err_suggest.stderr b/src/test/ui/suggestions/bool_typo_err_suggest.stderr new file mode 100644 index 0000000000000..52bde07ca0770 --- /dev/null +++ b/src/test/ui/suggestions/bool_typo_err_suggest.stderr @@ -0,0 +1,25 @@ +error[E0425]: cannot find value `True` in this scope + --> $DIR/bool_typo_err_suggest.rs:5:13 + | +LL | let x = True; + | ^^^^ not found in this scope + | +help: you may want to use a bool value instead + | +LL | let x = true; + | ~~~~ + +error[E0425]: cannot find value `False` in this scope + --> $DIR/bool_typo_err_suggest.rs:9:13 + | +LL | let y = False; + | ^^^^^ not found in this scope + | +help: you may want to use a bool value instead + | +LL | let y = false; + | ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. From 2a8fd5112489dcdf1d82f0c3ba40a2e0cbfc81b9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 23 Aug 2022 00:07:26 +0000 Subject: [PATCH 03/12] Make `const_eval_select` a real intrinsic --- compiler/rustc_codegen_ssa/src/mir/block.rs | 3 +- .../src/const_eval/machine.rs | 16 +- compiler/rustc_hir/src/lang_items.rs | 2 - .../rustc_middle/src/ty/consts/valtree.rs | 5 +- compiler/rustc_middle/src/ty/layout.rs | 175 +++++++++--------- compiler/rustc_mir_transform/src/lib.rs | 69 ++++++- compiler/rustc_monomorphize/src/collector.rs | 8 +- compiler/rustc_span/src/symbol.rs | 1 - .../rustc_typeck/src/check/fn_ctxt/checks.rs | 33 +++- library/core/src/intrinsics.rs | 143 +++++++------- library/core/src/mem/valid_align.rs | 2 +- library/core/src/num/f32.rs | 22 ++- library/core/src/num/f64.rs | 22 ++- library/core/src/num/nonzero.rs | 2 +- library/core/src/ptr/const_ptr.rs | 5 +- library/core/src/ptr/mod.rs | 8 +- library/core/src/slice/index.rs | 20 +- library/core/src/slice/mod.rs | 15 +- library/core/src/slice/raw.rs | 4 +- library/core/src/str/mod.rs | 2 + .../const-float-bits-reject-conv.stderr | 40 ---- .../const-eval-select-backtrace-std.rs | 6 + ...const-eval-select-backtrace-std.run.stderr | 2 + .../intrinsics/const-eval-select-backtrace.rs | 18 ++ .../const-eval-select-backtrace.run.stderr | 2 + .../ui/intrinsics/const-eval-select-bad.rs | 12 +- .../intrinsics/const-eval-select-bad.stderr | 79 +++++--- 27 files changed, 431 insertions(+), 285 deletions(-) create mode 100644 src/test/ui/intrinsics/const-eval-select-backtrace-std.rs create mode 100644 src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr create mode 100644 src/test/ui/intrinsics/const-eval-select-backtrace.rs create mode 100644 src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 3eee58d9d1c84..28caafe05a8f3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -13,8 +13,7 @@ use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::lang_items::LangItem; use rustc_index::vec::Idx; -use rustc_middle::mir::AssertKind; -use rustc_middle::mir::{self, SwitchTargets}; +use rustc_middle::mir::{self, AssertKind, SwitchTargets}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitable}; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 684877cae7677..f2a515e90d047 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -35,21 +35,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { // All `#[rustc_do_not_const_check]` functions should be hooked here. let def_id = instance.def_id(); - if Some(def_id) == self.tcx.lang_items().const_eval_select() { - // redirect to const_eval_select_ct - if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() { - return Ok(Some( - ty::Instance::resolve( - *self.tcx, - ty::ParamEnv::reveal_all(), - const_eval_select, - instance.substs, - ) - .unwrap() - .unwrap(), - )); - } - } else if Some(def_id) == self.tcx.lang_items().panic_display() + if Some(def_id) == self.tcx.lang_items().panic_display() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { // &str or &&str diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c337be12ae492..eb950920d5319 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -267,8 +267,6 @@ language_item_table! { DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); Oom, sym::oom, oom, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; - ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4); - ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4); Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 93707bb18ceec..a803fca0d5b89 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -18,7 +18,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; /// `ValTree` does not have this problem with representation, as it only contains integers or /// lists of (nested) `ValTree`. pub enum ValTree<'tcx> { - /// ZSTs, integers, `bool`, `char` are represented as scalars. + /// integers, `bool`, `char` are represented as scalars. /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values /// of these types have the same representation. Leaf(ScalarInt), @@ -27,8 +27,11 @@ pub enum ValTree<'tcx> { // dont use SliceOrStr for now /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by /// listing their fields' values in order. + /// /// Enums are represented by storing their discriminant as a field, followed by all /// the fields of the variant. + /// + /// ZST types are represented as an empty slice. Branch(&'tcx [ValTree<'tcx>]), } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f5505590168d2..fd7dfd5816cde 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -274,6 +274,7 @@ fn layout_of<'tcx>( }) } +#[derive(Clone, Copy)] pub struct LayoutCx<'tcx, C> { pub tcx: C, pub param_env: ty::ParamEnv<'tcx>, @@ -3074,6 +3075,93 @@ fn fn_abi_of_instance<'tcx>( ) } +// Handle safe Rust thin and fat pointers. +pub fn adjust_for_rust_scalar<'tcx>( + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + attrs: &mut ArgAttributes, + scalar: Scalar, + layout: TyAndLayout<'tcx>, + offset: Size, + is_return: bool, +) { + // Booleans are always a noundef i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.ext(ArgExtension::Zext); + attrs.set(ArgAttribute::NoUndef); + return; + } + + // Scalars which have invalid values cannot be undef. + if !scalar.is_always_valid(&cx) { + attrs.set(ArgAttribute::NoUndef); + } + + // Only pointer types handled below. + let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return }; + + if !valid_range.contains(0) { + attrs.set(ArgAttribute::NonNull); + } + + if let Some(pointee) = layout.pointee_info_at(&cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_align = Some(pointee.align); + + // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable + // for the entire duration of the function as they can be deallocated + // at any time. Same for shared mutable references. If LLVM had a + // way to say "dereferenceable on entry" we could use it here. + attrs.pointee_size = match kind { + PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned + | PointerKind::Frozen => pointee.size, + PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, + }; + + // `Box`, `&T`, and `&mut T` cannot be undef. + // Note that this only applies to the value of the pointer itself; + // this attribute doesn't make it UB for the pointed-to data to be undef. + attrs.set(ArgAttribute::NoUndef); + + // The aliasing rules for `Box` are still not decided, but currently we emit + // `noalias` for it. This can be turned off using an unstable flag. + // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 + let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true); + + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + // + // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute + // for UniqueBorrowed arguments, so that the codegen backend can decide whether + // or not to actually emit the attribute. It can also be controlled with the + // `-Zmutable-noalias` debugging option. + let no_alias = match kind { + PointerKind::SharedMutable + | PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned => false, + PointerKind::UniqueOwned => noalias_for_box, + PointerKind::Frozen => !is_return, + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + + if kind == PointerKind::UniqueBorrowed && !is_return { + attrs.set(ArgAttribute::NoAliasMutRef); + } + } + } +} + impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // FIXME(eddyb) perhaps group the signature/type-containing (or all of them?) // arguments of this method, into a separate `struct`. @@ -3129,91 +3217,6 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { use SpecAbi::*; let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall); - // Handle safe Rust thin and fat pointers. - let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, - scalar: Scalar, - layout: TyAndLayout<'tcx>, - offset: Size, - is_return: bool| { - // Booleans are always a noundef i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.ext(ArgExtension::Zext); - attrs.set(ArgAttribute::NoUndef); - return; - } - - // Scalars which have invalid values cannot be undef. - if !scalar.is_always_valid(self) { - attrs.set(ArgAttribute::NoUndef); - } - - // Only pointer types handled below. - let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return }; - - if !valid_range.contains(0) { - attrs.set(ArgAttribute::NonNull); - } - - if let Some(pointee) = layout.pointee_info_at(self, offset) { - if let Some(kind) = pointee.safe { - attrs.pointee_align = Some(pointee.align); - - // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable - // for the entire duration of the function as they can be deallocated - // at any time. Same for shared mutable references. If LLVM had a - // way to say "dereferenceable on entry" we could use it here. - attrs.pointee_size = match kind { - PointerKind::UniqueBorrowed - | PointerKind::UniqueBorrowedPinned - | PointerKind::Frozen => pointee.size, - PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, - }; - - // `Box`, `&T`, and `&mut T` cannot be undef. - // Note that this only applies to the value of the pointer itself; - // this attribute doesn't make it UB for the pointed-to data to be undef. - attrs.set(ArgAttribute::NoUndef); - - // The aliasing rules for `Box` are still not decided, but currently we emit - // `noalias` for it. This can be turned off using an unstable flag. - // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 - let noalias_for_box = - self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true); - - // `&mut` pointer parameters never alias other parameters, - // or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, - // and can be marked as both `readonly` and `noalias`, as - // LLVM's definition of `noalias` is based solely on memory - // dependencies rather than pointer equality - // - // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute - // for UniqueBorrowed arguments, so that the codegen backend can decide whether - // or not to actually emit the attribute. It can also be controlled with the - // `-Zmutable-noalias` debugging option. - let no_alias = match kind { - PointerKind::SharedMutable - | PointerKind::UniqueBorrowed - | PointerKind::UniqueBorrowedPinned => false, - PointerKind::UniqueOwned => noalias_for_box, - PointerKind::Frozen => !is_return, - }; - if no_alias { - attrs.set(ArgAttribute::NoAlias); - } - - if kind == PointerKind::Frozen && !is_return { - attrs.set(ArgAttribute::ReadOnly); - } - - if kind == PointerKind::UniqueBorrowed && !is_return { - attrs.set(ArgAttribute::NoAliasMutRef); - } - } - } - }; - let arg_of = |ty: Ty<'tcx>, arg_idx: Option| -> Result<_, FnAbiError<'tcx>> { let is_return = arg_idx.is_none(); @@ -3229,7 +3232,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| { let mut attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return); + adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return); attrs }); diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 56fb601d2b535..04e0f10716e40 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -9,6 +9,7 @@ #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(if_let_guard)] #![recursion_limit = "256"] #[macro_use] @@ -25,9 +26,13 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; -use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted}; +use rustc_middle::mir::{ + traversal, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand, Place, + ProjectionElem, Promoted, Rvalue, SourceInfo, Statement, StatementKind, TerminatorKind, +}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_span::sym; #[macro_use] mod pass_manager; @@ -137,6 +142,64 @@ pub fn provide(providers: &mut Providers) { }; } +fn remap_mir_for_const_eval_select<'tcx>( + tcx: TyCtxt<'tcx>, + mut body: Body<'tcx>, + context: hir::Constness, +) -> Body<'tcx> { + for bb in body.basic_blocks.as_mut().iter_mut() { + let terminator = bb.terminator.as_mut().expect("invalid terminator"); + match terminator.kind { + TerminatorKind::Call { + func: Operand::Constant(box Constant { ref literal, .. }), + ref mut args, + destination, + target, + cleanup, + fn_span, + .. + } if let ty::FnDef(def_id, _) = *literal.ty().kind() + && tcx.item_name(def_id) == sym::const_eval_select + && tcx.is_intrinsic(def_id) => + { + let [tupled_args, called_in_const, called_at_rt]: [_; 3] = std::mem::take(args).try_into().unwrap(); + let ty = tupled_args.ty(&body.local_decls, tcx); + let fields = ty.tuple_fields(); + let num_args = fields.len(); + let func = if context == hir::Constness::Const { called_in_const } else { called_at_rt }; + let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args { + Operand::Constant(_) => { + // there is no good way of extracting a tuple arg from a constant (const generic stuff) + // so we just create a temporary and deconstruct that. + let local = body.local_decls.push(LocalDecl::new(ty, fn_span)); + bb.statements.push(Statement { + source_info: SourceInfo::outermost(fn_span), + kind: StatementKind::Assign(Box::new((local.into(), Rvalue::Use(tupled_args.clone())))), + }); + (Operand::Move, local.into()) + } + Operand::Move(place) => (Operand::Move, place), + Operand::Copy(place) => (Operand::Copy, place), + }; + let place_elems = place.projection; + let arguments = (0..num_args).map(|x| { + let mut place_elems = place_elems.to_vec(); + place_elems.push(ProjectionElem::Field(x.into(), fields[x])); + let projection = tcx.intern_place_elems(&place_elems); + let place = Place { + local: place.local, + projection, + }; + method(place) + }).collect(); + terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, cleanup, from_hir_call: false, fn_span }; + } + _ => {} + } + } + body +} + fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let def_id = def_id.expect_local(); tcx.mir_keys(()).contains(&def_id) @@ -347,7 +410,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE"); - body + remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const) } /// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs @@ -537,7 +600,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); - body + remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst) } /// Fetch all the promoteds of an item and prepare their MIR bodies to be ready for diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 82ef16a7f72fc..1efcbeacbb912 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -112,12 +112,6 @@ //! method in operand position, we treat it as a neighbor of the current //! mono item. Calls are just a special case of that. //! -//! #### Closures -//! In a way, closures are a simple case. Since every closure object needs to be -//! constructed somewhere, we can reliably discover them by observing -//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also -//! true for closures inlined from other crates. -//! //! #### Drop glue //! Drop glue mono items are introduced by MIR drop-statements. The //! generated mono item will again have drop-glue item neighbors if the @@ -830,7 +824,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.body, tcx); let callee_ty = self.monomorphize(callee_ty); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output); + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) } mir::TerminatorKind::Drop { ref place, .. } | mir::TerminatorKind::DropAndReplace { ref place, .. } => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 156f53ac48626..f6a3f5ca87f98 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -504,7 +504,6 @@ symbols! { const_deallocate, const_eval_limit, const_eval_select, - const_eval_select_ct, const_evaluatable_checked, const_extern_fn, const_fn, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 3642b2ab03bfa..2b59ec7dcdffc 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; +use rustc_span::{self, sym, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; @@ -224,6 +224,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let minimum_input_count = expected_input_tys.len(); let provided_arg_count = provided_args.len(); + let is_const_eval_select = matches!(fn_def_id, Some(def_id) if + self.tcx.def_kind(def_id) == hir::def::DefKind::Fn + && self.tcx.is_intrinsic(def_id) + && self.tcx.item_name(def_id) == sym::const_eval_select); + // We introduce a helper function to demand that a given argument satisfy a given input // This is more complicated than just checking type equality, as arguments could be coerced // This version writes those types back so further type checking uses the narrowed types @@ -259,6 +264,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Compatibility::Incompatible(coerce_error); } + // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that + // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed + // in the function signature (`F: FnOnce`), so I did not bother to add another check here. + // + // This check is here because there is currently no way to express a trait bound for `FnDef` types only. + if is_const_eval_select && (1..=2).contains(&idx) { + if let ty::FnDef(def_id, _) = checked_ty.kind() { + if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a `const fn`") + .help("consult the documentation on `const_eval_select` for more information") + .emit(); + } + } else { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a function item") + .note(format!("expected a function item, found {checked_ty}")) + .help( + "consult the documentation on `const_eval_select` for more information", + ) + .emit(); + } + } + // 3. Check if the formal type is a supertype of the checked one // and register any such obligations for future type checks let supertype_error = self diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3d7b875eac15d..ee3e67f88ba75 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -54,7 +54,9 @@ )] #![allow(missing_docs)] -use crate::marker::{Destruct, DiscriminantKind}; +#[cfg(bootstrap)] +use crate::marker::Destruct; +use crate::marker::DiscriminantKind; use crate::mem; // These imports are used for simplifying intra-doc links @@ -2095,6 +2097,65 @@ extern "rust-intrinsic" { /// `ptr` must point to a vtable. /// The intrinsic will return the alignment stored in that vtable. pub fn vtable_align(ptr: *const ()) -> usize; + + /// Selects which function to call depending on the context. + /// + /// If this function is evaluated at compile-time, then a call to this + /// intrinsic will be replaced with a call to `called_in_const`. It gets + /// replaced with a call to `called_at_rt` otherwise. + /// + /// # Type Requirements + /// + /// The two functions must be both function items. They cannot be function + /// pointers or closures. The first function must be a `const fn`. + /// + /// `arg` will be the tupled arguments that will be passed to either one of + /// the two functions, therefore, both functions must accept the same type of + /// arguments. Both functions must return RET. + /// + /// # Safety + /// + /// The two functions must behave observably equivalent. Safe code in other + /// crates may assume that calling a `const fn` at compile-time and at run-time + /// produces the same result. A function that produces a different result when + /// evaluated at run-time, or has any other observable side-effects, is + /// *unsound*. + /// + /// Here is an example of how this could cause a problem: + /// ```no_run + /// #![feature(const_eval_select)] + /// #![feature(core_intrinsics)] + /// use std::hint::unreachable_unchecked; + /// use std::intrinsics::const_eval_select; + /// + /// // Crate A + /// pub const fn inconsistent() -> i32 { + /// fn runtime() -> i32 { 1 } + /// const fn compiletime() -> i32 { 2 } + /// + /// unsafe { + // // âš  This code violates the required equivalence of `compiletime` + /// // and `runtime`. + /// const_eval_select((), compiletime, runtime) + /// } + /// } + /// + /// // Crate B + /// const X: i32 = inconsistent(); + /// let x = inconsistent(); + /// if x != X { unsafe { unreachable_unchecked(); }} + /// ``` + /// + /// This code causes Undefined Behavior when being run, since the + /// `unreachable_unchecked` is actually being reached. The bug is in *crate A*, + /// which violates the principle that a `const fn` must behave the same at + /// compile-time and at run-time. The unsafe code in crate B is fine. + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_eval_select", issue = "none")] + pub fn const_eval_select(arg: ARG, called_in_const: F, called_at_rt: G) -> RET + where + G: FnOnce, + F: FnOnce; } // Some functions are defined here because they accidentally got made @@ -2105,6 +2166,11 @@ extern "rust-intrinsic" { /// Check that the preconditions of an unsafe function are followed, if debug_assertions are on, /// and only at runtime. /// +/// This macro should be called as `assert_unsafe_precondition!([Generics](name: Type) => Expression)` +/// where the names specified will be moved into the macro as captured variables, and defines an item +/// to call `const_eval_select` on. The tokens inside the square brackets are used to denote generics +/// for the function declaractions and can be omitted if there is no generics. +/// /// # Safety /// /// Invoking this macro is only sound if the following code is already UB when the passed @@ -2119,18 +2185,20 @@ extern "rust-intrinsic" { /// the occasional mistake, and this check should help them figure things out. #[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn macro_rules! assert_unsafe_precondition { - ($e:expr) => { + ($([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => { if cfg!(debug_assertions) { - // Use a closure so that we can capture arbitrary expressions from the invocation - let runtime = || { + // allow non_snake_case to allow capturing const generics + #[allow(non_snake_case)] + fn runtime$(<$($tt)*>)?($($i:$ty),*) { if !$e { // abort instead of panicking to reduce impact on code size ::core::intrinsics::abort(); } - }; - const fn comptime() {} + } + #[allow(non_snake_case)] + const fn comptime$(<$($tt)*>)?($(_:$ty),*) {} - ::core::intrinsics::const_eval_select((), comptime, runtime); + ::core::intrinsics::const_eval_select(($($i,)*), comptime, runtime); } }; } @@ -2253,7 +2321,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us // SAFETY: the safety contract for `copy_nonoverlapping` must be // upheld by the caller. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](src: *const T, dst: *mut T, count: usize) => is_aligned_and_not_null(src) && is_aligned_and_not_null(dst) && is_nonoverlapping(src, dst, count) @@ -2339,7 +2407,8 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](src: *const T, dst: *mut T) => + is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)); copy(src, dst, count) } } @@ -2407,63 +2476,12 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst)); write_bytes(dst, val, count) } } -/// Selects which function to call depending on the context. -/// -/// If this function is evaluated at compile-time, then a call to this -/// intrinsic will be replaced with a call to `called_in_const`. It gets -/// replaced with a call to `called_at_rt` otherwise. -/// -/// # Type Requirements -/// -/// The two functions must be both function items. They cannot be function -/// pointers or closures. -/// -/// `arg` will be the arguments that will be passed to either one of the -/// two functions, therefore, both functions must accept the same type of -/// arguments. Both functions must return RET. -/// -/// # Safety -/// -/// The two functions must behave observably equivalent. Safe code in other -/// crates may assume that calling a `const fn` at compile-time and at run-time -/// produces the same result. A function that produces a different result when -/// evaluated at run-time, or has any other observable side-effects, is -/// *unsound*. -/// -/// Here is an example of how this could cause a problem: -/// ```no_run -/// #![feature(const_eval_select)] -/// #![feature(core_intrinsics)] -/// use std::hint::unreachable_unchecked; -/// use std::intrinsics::const_eval_select; -/// -/// // Crate A -/// pub const fn inconsistent() -> i32 { -/// fn runtime() -> i32 { 1 } -/// const fn compiletime() -> i32 { 2 } -/// -/// unsafe { -// // âš  This code violates the required equivalence of `compiletime` -/// // and `runtime`. -/// const_eval_select((), compiletime, runtime) -/// } -/// } -/// -/// // Crate B -/// const X: i32 = inconsistent(); -/// let x = inconsistent(); -/// if x != X { unsafe { unreachable_unchecked(); }} -/// ``` -/// -/// This code causes Undefined Behavior when being run, since the -/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*, -/// which violates the principle that a `const fn` must behave the same at -/// compile-time and at run-time. The unsafe code in crate B is fine. +#[cfg(bootstrap)] #[unstable( feature = "const_eval_select", issue = "none", @@ -2485,6 +2503,7 @@ where called_at_rt.call_once(arg) } +#[cfg(bootstrap)] #[unstable( feature = "const_eval_select", issue = "none", diff --git a/library/core/src/mem/valid_align.rs b/library/core/src/mem/valid_align.rs index b9ccc0b4c799f..32b2afb72b0dc 100644 --- a/library/core/src/mem/valid_align.rs +++ b/library/core/src/mem/valid_align.rs @@ -28,7 +28,7 @@ impl ValidAlign { #[inline] pub(crate) const unsafe fn new_unchecked(align: usize) -> Self { // SAFETY: Precondition passed to the caller. - unsafe { assert_unsafe_precondition!(align.is_power_of_two()) }; + unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) }; // SAFETY: By precondition, this must be a power of two, and // our variants encompass all possible powers of two. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index efa434f239266..69b72972713b9 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -933,10 +933,13 @@ impl f32 { } } } - // SAFETY: `u32` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_f32_to_u32 = |rt| unsafe { mem::transmute::(rt) }; + + fn rt_f32_to_u32(x: f32) -> u32 { + // SAFETY: `u32` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } } @@ -1021,10 +1024,13 @@ impl f32 { } } } - // SAFETY: `u32` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_u32_to_f32 = |rt| unsafe { mem::transmute::(rt) }; + + fn rt_u32_to_f32(x: u32) -> f32 { + // SAFETY: `u32` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 9e4334fe01ad8..8eef714e25c9e 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -926,10 +926,13 @@ impl f64 { } } } - // SAFETY: `u64` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_f64_to_u64 = |rt| unsafe { mem::transmute::(rt) }; + + fn rt_f64_to_u64(rt: f64) -> u64 { + // SAFETY: `u64` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute::(rt) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } } @@ -1019,10 +1022,13 @@ impl f64 { } } } - // SAFETY: `u64` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - let rt_u64_to_f64 = |rt| unsafe { mem::transmute::(rt) }; + + fn rt_u64_to_f64(rt: u64) -> f64 { + // SAFETY: `u64` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute::(rt) } + } // SAFETY: We use internal implementations that either always work or fail at compile time. unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) } } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 6196c4da4e329..7dc8dd83ea030 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -56,7 +56,7 @@ macro_rules! nonzero_integers { pub const unsafe fn new_unchecked(n: $Int) -> Self { // SAFETY: this is guaranteed to be safe by the caller. unsafe { - core::intrinsics::assert_unsafe_precondition!(n != 0); + core::intrinsics::assert_unsafe_precondition!((n: $Int) => n != 0); Self(n) } } diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index c25b159c533a1..bf0e87867a1ce 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -740,9 +740,12 @@ impl *const T { where T: Sized, { + let this = self; // SAFETY: The comparison has no side-effects, and the intrinsic // does this check internally in the CTFE implementation. - unsafe { assert_unsafe_precondition!(self >= origin) }; + unsafe { + assert_unsafe_precondition!([T](this: *const T, origin: *const T) => this >= origin) + }; let pointee_size = mem::size_of::(); assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 203531f66aa36..baa8b2bac3e3c 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -886,7 +886,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { // SAFETY: the caller must guarantee that `x` and `y` are // valid for writes and properly aligned. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](x: *mut T, y: *mut T, count: usize) => is_aligned_and_not_null(x) && is_aligned_and_not_null(y) && is_nonoverlapping(x, y, count) @@ -983,7 +983,7 @@ pub const unsafe fn replace(dst: *mut T, mut src: T) -> T { // and cannot overlap `src` since `dst` must point to a distinct // allocated object. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst)); mem::swap(&mut *dst, &mut src); // cannot overlap } src @@ -1470,7 +1470,7 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { pub unsafe fn read_volatile(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(src)); + assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src)); intrinsics::volatile_load(src) } } @@ -1541,7 +1541,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { pub unsafe fn write_volatile(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { - assert_unsafe_precondition!(is_aligned_and_not_null(dst)); + assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst)); intrinsics::volatile_store(dst, src); } } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index fd7ecf3daf316..3403a5a86f7cb 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -48,10 +48,12 @@ const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { } // FIXME const-hack +#[track_caller] fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range start index {index} out of range for slice of length {len}"); } +#[track_caller] const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! { panic!("slice start index is out of range for slice"); } @@ -69,10 +71,12 @@ const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { } // FIXME const-hack +#[track_caller] fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range end index {index} out of range for slice of length {len}"); } +#[track_caller] const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! { panic!("slice end index is out of range for slice"); } @@ -88,10 +92,12 @@ const fn slice_index_order_fail(index: usize, end: usize) -> ! { } // FIXME const-hack +#[track_caller] fn slice_index_order_fail_rt(index: usize, end: usize) -> ! { panic!("slice index starts at {index} but ends at {end}"); } +#[track_caller] const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! { panic!("slice index start is larger than end"); } @@ -217,21 +223,23 @@ unsafe impl const SliceIndex<[T]> for usize { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { + let this = self; // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { - assert_unsafe_precondition!(self < slice.len()); + assert_unsafe_precondition!([T](this: usize, slice: *const [T]) => this < slice.len()); slice.as_ptr().add(self) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { + let this = self; // SAFETY: see comments for `get_unchecked` above. unsafe { - assert_unsafe_precondition!(self < slice.len()); + assert_unsafe_precondition!([T](this: usize, slice: *mut [T]) => this < slice.len()); slice.as_mut_ptr().add(self) } } @@ -276,22 +284,26 @@ unsafe impl const SliceIndex<[T]> for ops::Range { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + let this = ops::Range { start: self.start, end: self.end }; // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { - assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len()); + assert_unsafe_precondition!([T](this: ops::Range, slice: *const [T]) => + this.end >= this.start && this.end <= slice.len()); ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + let this = ops::Range { start: self.start, end: self.end }; // SAFETY: see comments for `get_unchecked` above. unsafe { - assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len()); + assert_unsafe_precondition!([T](this: ops::Range, slice: *mut [T]) => + this.end >= this.start && this.end <= slice.len()); ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) } } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ea6a70c2f4e74..8be88580d9570 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -656,10 +656,11 @@ impl [T] { #[unstable(feature = "slice_swap_unchecked", issue = "88539")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { - let ptr = self.as_mut_ptr(); + let this = self; + let ptr = this.as_mut_ptr(); // SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()` unsafe { - assert_unsafe_precondition!(a < self.len() && b < self.len()); + assert_unsafe_precondition!([T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len()); ptr::swap(ptr.add(a), ptr.add(b)); } } @@ -972,9 +973,10 @@ impl [T] { #[inline] #[must_use] pub unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { + let this = self; // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { - assert_unsafe_precondition!(N != 0 && self.len() % N == 0); + assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0); exact_div(self.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into @@ -1111,10 +1113,11 @@ impl [T] { #[inline] #[must_use] pub unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { + let this = &*self; // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { - assert_unsafe_precondition!(N != 0 && self.len() % N == 0); - exact_div(self.len(), N) + assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0); + exact_div(this.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. @@ -1678,7 +1681,7 @@ impl [T] { // `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference // is fine. unsafe { - assert_unsafe_precondition!(mid <= len); + assert_unsafe_precondition!((mid: usize, len: usize) => mid <= len); (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) } } diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 107e71ab68b01..f1e8bc79bf4a2 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -90,7 +90,7 @@ use crate::ptr; pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](data: *const T, len: usize) => is_aligned_and_not_null(data) && crate::mem::size_of::().saturating_mul(len) <= isize::MAX as usize ); @@ -134,7 +134,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { - assert_unsafe_precondition!( + assert_unsafe_precondition!([T](data: *mut T, len: usize) => is_aligned_and_not_null(data) && crate::mem::size_of::().saturating_mul(len) <= isize::MAX as usize ); diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index e44e025aaf169..653d28f0c3ab0 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -91,10 +91,12 @@ const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { } } +#[track_caller] const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! { panic!("failed to slice string"); } +#[track_caller] fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH); diff --git a/src/test/ui/consts/const-float-bits-reject-conv.stderr b/src/test/ui/consts/const-float-bits-reject-conv.stderr index d6e993a10101d..01f2f48956458 100644 --- a/src/test/ui/consts/const-float-bits-reject-conv.stderr +++ b/src/test/ui/consts/const-float-bits-reject-conv.stderr @@ -10,16 +10,6 @@ LL | panic!("const-eval error: cannot use f32::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } | -------------------------------------------------------------------- inside `core::f32::::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside ` u32 {core::f32::::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::::to_bits::ct_f32_to_u32}, [closure@core::f32::::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:27:30 | LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA; @@ -39,16 +29,6 @@ LL | panic!("const-eval error: cannot use f32::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } | -------------------------------------------------------------------- inside `core::f32::::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside ` u32 {core::f32::::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::::to_bits::ct_f32_to_u32}, [closure@core::f32::::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:28:30 | LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555; @@ -117,16 +97,6 @@ LL | panic!("const-eval error: cannot use f64::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } | -------------------------------------------------------------------- inside `core::f64::::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside ` u64 {core::f64::::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::::to_bits::ct_f64_to_u64}, [closure@core::f64::::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:54:30 | LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; @@ -146,16 +116,6 @@ LL | panic!("const-eval error: cannot use f64::to_bits on a LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } | -------------------------------------------------------------------- inside `core::f64::::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL | - ::: $SRC_DIR/core/src/ops/function.rs:LL:COL - | -LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ------------------------------------------------------------------ inside ` u64 {core::f64::::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL - | - ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL - | -LL | called_in_const.call_once(arg) - | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::::to_bits::ct_f64_to_u64}, [closure@core::f64::::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | ::: $DIR/const-float-bits-reject-conv.rs:55:30 | LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace-std.rs b/src/test/ui/intrinsics/const-eval-select-backtrace-std.rs new file mode 100644 index 0000000000000..29aefe07162b3 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace-std.rs @@ -0,0 +1,6 @@ +// See issue #100696. +// run-fail +// check-run-results +fn main() { + &""[1..]; +} diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr b/src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr new file mode 100644 index 0000000000000..e53e6034620ca --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace-std.run.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'byte index 1 is out of bounds of ``', $DIR/const-eval-select-backtrace-std.rs:5:6 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace.rs b/src/test/ui/intrinsics/const-eval-select-backtrace.rs new file mode 100644 index 0000000000000..99f0725200c1d --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace.rs @@ -0,0 +1,18 @@ +#![feature(core_intrinsics)] +// See issue #100696. +// run-fail +// check-run-results + +#[track_caller] +fn uhoh() { + panic!("Aaah!") +} + +const fn c() {} + +fn main() { + // safety: this is unsound and just used to test + unsafe { + std::intrinsics::const_eval_select((), c, uhoh); + } +} diff --git a/src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr b/src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr new file mode 100644 index 0000000000000..2fd730ac7a68a --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-backtrace.run.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'Aaah!', $DIR/const-eval-select-backtrace.rs:16:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/test/ui/intrinsics/const-eval-select-bad.rs b/src/test/ui/intrinsics/const-eval-select-bad.rs index e5640f5ab53b1..fa14efad7b4a4 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.rs +++ b/src/test/ui/intrinsics/const-eval-select-bad.rs @@ -5,10 +5,13 @@ use std::intrinsics::const_eval_select; const fn not_fn_items() { const_eval_select((), || {}, || {}); - //~^ ERROR the trait bound + //~^ ERROR this argument must be a function item + //~| ERROR this argument must be a function item const_eval_select((), 42, 0xDEADBEEF); - //~^ ERROR the trait bound + //~^ ERROR expected a `FnOnce<()>` closure //~| ERROR expected a `FnOnce<()>` closure + //~| ERROR this argument must be a function item + //~| ERROR this argument must be a function item } const fn foo(n: i32) -> i32 { @@ -35,4 +38,9 @@ const fn args_ty_mismatch() { //~^ ERROR type mismatch } +const fn non_const_fn() { + const_eval_select((1,), bar, bar); + //~^ ERROR this argument must be a `const fn` +} + fn main() {} diff --git a/src/test/ui/intrinsics/const-eval-select-bad.stderr b/src/test/ui/intrinsics/const-eval-select-bad.stderr index 1a761ad5441ce..3720528ad4e40 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.stderr +++ b/src/test/ui/intrinsics/const-eval-select-bad.stderr @@ -1,42 +1,57 @@ -error[E0277]: the trait bound `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]: FnOnce<()>` is not satisfied +error: this argument must be a function item --> $DIR/const-eval-select-bad.rs:7:27 | LL | const_eval_select((), || {}, || {}); - | ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` - | | - | required by a bound introduced by this call + | ^^^^^ | - = help: the trait `~const FnOnce<()>` is not implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` -note: the trait `FnOnce<()>` is implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`, but that implementation is not `const` - --> $DIR/const-eval-select-bad.rs:7:27 + = note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:27: 7:29] + = help: consult the documentation on `const_eval_select` for more information + +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:7:34 | LL | const_eval_select((), || {}, || {}); - | ^^^^^ - = note: wrap the `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` in a closure with no arguments: `|| { /* code */ }` -note: required by a bound in `const_eval_select` - --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | ^^^^^ | -LL | F: ~const FnOnce, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + = note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:34: 7:36] + = help: consult the documentation on `const_eval_select` for more information -error[E0277]: the trait bound `{integer}: FnOnce<()>` is not satisfied - --> $DIR/const-eval-select-bad.rs:9:27 +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:10:27 + | +LL | const_eval_select((), 42, 0xDEADBEEF); + | ^^ + | + = note: expected a function item, found {integer} + = help: consult the documentation on `const_eval_select` for more information + +error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` + --> $DIR/const-eval-select-bad.rs:10:27 | LL | const_eval_select((), 42, 0xDEADBEEF); | ----------------- ^^ expected an `FnOnce<()>` closure, found `{integer}` | | | required by a bound introduced by this call | - = help: the trait `~const FnOnce<()>` is not implemented for `{integer}` + = help: the trait `FnOnce<()>` is not implemented for `{integer}` = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | F: ~const FnOnce, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: FnOnce; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:10:31 + | +LL | const_eval_select((), 42, 0xDEADBEEF); + | ^^^^^^^^^^ + | + = note: expected a function item, found {integer} + = help: consult the documentation on `const_eval_select` for more information error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` - --> $DIR/const-eval-select-bad.rs:9:31 + --> $DIR/const-eval-select-bad.rs:10:31 | LL | const_eval_select((), 42, 0xDEADBEEF); | ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}` @@ -48,11 +63,11 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | G: FnOnce + ~const Destruct, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | G: FnOnce, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0271]: expected `fn(i32) -> bool {bar}` to be a fn item that returns `i32`, but it returns `bool` - --> $DIR/const-eval-select-bad.rs:29:34 + --> $DIR/const-eval-select-bad.rs:32:34 | LL | const_eval_select((1,), foo, bar); | ----------------- ^^^ expected `i32`, found `bool` @@ -62,11 +77,11 @@ LL | const_eval_select((1,), foo, bar); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | G: FnOnce + ~const Destruct, - | ^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | G: FnOnce, + | ^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0631]: type mismatch in function arguments - --> $DIR/const-eval-select-bad.rs:34:32 + --> $DIR/const-eval-select-bad.rs:37:32 | LL | const fn foo(n: i32) -> i32 { | --------------------------- found signature defined here @@ -81,10 +96,18 @@ LL | const_eval_select((true,), foo, baz); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | F: ~const FnOnce, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: FnOnce; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + +error: this argument must be a `const fn` + --> $DIR/const-eval-select-bad.rs:42:29 + | +LL | const_eval_select((1,), bar, bar); + | ^^^ + | + = help: consult the documentation on `const_eval_select` for more information -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0271, E0277, E0631. For more information about an error, try `rustc --explain E0271`. From cb4cd7366415deb659641edcff695aa2e4ffb105 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Aug 2022 21:25:03 -0400 Subject: [PATCH 04/12] extra sanity check against consts pointing to mutable memory --- .../src/interpret/validity.rs | 72 ++++++++++++------- .../const-points-to-static.32bit.stderr | 2 +- .../const-points-to-static.64bit.stderr | 2 +- .../const_refers_to_static2.32bit.stderr | 4 +- .../const_refers_to_static2.64bit.stderr | 4 +- ..._refers_to_static_cross_crate.32bit.stderr | 4 +- ..._refers_to_static_cross_crate.64bit.stderr | 4 +- 7 files changed, 55 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index f1b1855c3ec74..0e60f0c7ef131 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -8,6 +8,7 @@ use std::convert::TryFrom; use std::fmt::Write; use std::num::NonZeroUsize; +use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::mir::interpret::InterpError; @@ -411,34 +412,51 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) { - // Special handling for pointers to statics (irrespective of their type). + // Let's see what kind of memory this points to. let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id); - if let Some(GlobalAlloc::Static(did)) = alloc_kind { - assert!(!self.ecx.tcx.is_thread_local_static(did)); - assert!(self.ecx.tcx.is_static(did)); - if matches!( - self.ctfe_mode, - Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. }) - ) { - // See const_eval::machine::MemoryExtra::can_access_statics for why - // this check is so important. - // This check is reachable when the const just referenced the static, - // but never read it (so we never entered `before_access_global`). - throw_validation_failure!(self.path, - { "a {} pointing to a static variable", kind } - ); + match alloc_kind { + Some(GlobalAlloc::Static(did)) => { + // Special handling for pointers to statics (irrespective of their type). + assert!(!self.ecx.tcx.is_thread_local_static(did)); + assert!(self.ecx.tcx.is_static(did)); + if matches!( + self.ctfe_mode, + Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. }) + ) { + // See const_eval::machine::MemoryExtra::can_access_statics for why + // this check is so important. + // This check is reachable when the const just referenced the static, + // but never read it (so we never entered `before_access_global`). + throw_validation_failure!(self.path, + { "a {} pointing to a static variable in a constant", kind } + ); + } + // We skip recursively checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us. + // We might miss const-invalid data, + // but things are still sound otherwise (in particular re: consts + // referring to statics). + return Ok(()); } - // We skip checking other statics. These statics must be sound by - // themselves, and the only way to get broken statics here is by using - // unsafe code. - // The reasons we don't check other statics is twofold. For one, in all - // sound cases, the static was already validated on its own, and second, we - // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us. - // We might miss const-invalid data, - // but things are still sound otherwise (in particular re: consts - // referring to statics). - return Ok(()); + Some(GlobalAlloc::Memory(alloc)) => { + if alloc.inner().mutability == Mutability::Mut + && matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) + { + // This should be unreachable, but if someone manages to copy a pointer + // out of a `static`, then that pointer might point to mutable memory, + // and we would catch that here. + throw_validation_failure!(self.path, + { "a {} pointing to mutable memory in a constant", kind } + ); + } + } + // Nothing to check for these. + None | Some(GlobalAlloc::Function(..) | GlobalAlloc::VTable(..)) => {} } } let path = &self.path; @@ -544,7 +562,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } ty::Ref(_, ty, mutbl) => { if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) - && *mutbl == hir::Mutability::Mut + && *mutbl == Mutability::Mut { // A mutable reference inside a const? That does not seem right (except if it is // a ZST). diff --git a/src/test/ui/consts/const-points-to-static.32bit.stderr b/src/test/ui/consts/const-points-to-static.32bit.stderr index 97825dd0eb517..c7a435a1ee3fc 100644 --- a/src/test/ui/consts/const-points-to-static.32bit.stderr +++ b/src/test/ui/consts/const-points-to-static.32bit.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-points-to-static.rs:6:1 | LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { diff --git a/src/test/ui/consts/const-points-to-static.64bit.stderr b/src/test/ui/consts/const-points-to-static.64bit.stderr index 0d4a5a8ce4f33..4d5b8eac541df 100644 --- a/src/test/ui/consts/const-points-to-static.64bit.stderr +++ b/src/test/ui/consts/const-points-to-static.64bit.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-points-to-static.rs:6:1 | LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr index b3ad81e49bc16..14173ac9f69b4 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static2.rs:11:1 | LL | const REF_INTERIOR_MUT: &usize = { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { @@ -13,7 +13,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static2.rs:18:1 | LL | const READ_IMMUT: &usize = { - | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr index 24bd070928265..e7e51a41856e2 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static2.rs:11:1 | LL | const REF_INTERIOR_MUT: &usize = { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { @@ -13,7 +13,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static2.rs:18:1 | LL | const READ_IMMUT: &usize = { - | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr index 20a96b57f29b9..3a22b06891629 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static_cross_crate.rs:12:1 | LL | const SLICE_MUT: &[u8; 1] = { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { @@ -19,7 +19,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static_cross_crate.rs:17:1 | LL | const U8_MUT: &u8 = { - | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr index dfa0f76baa186..39c874d6498b2 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static_cross_crate.rs:12:1 | LL | const SLICE_MUT: &[u8; 1] = { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { @@ -19,7 +19,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const_refers_to_static_cross_crate.rs:17:1 | LL | const U8_MUT: &u8 = { - | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable + | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { From 289d7cca1dff4b142b1c208fd1dd5eb2ce0a628f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Wed, 24 Aug 2022 00:53:09 +0200 Subject: [PATCH 05/12] Reduce code size of `assert_matches_failed` --- library/core/src/panicking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 7a575a88e52e1..d4afe0f5326a3 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -190,11 +190,11 @@ pub fn assert_matches_failed( right: &str, args: Option>, ) -> ! { - // Use the Display implementation to display the pattern. + // The pattern is a string so it can be displayed directly. struct Pattern<'a>(&'a str); impl fmt::Debug for Pattern<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.0, f) + f.write_str(self.0) } } assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args); From 71193954f900463c9b00e3485d3430990dce3426 Mon Sep 17 00:00:00 2001 From: Luis Cardoso <61982523+LuisCardosoOliveira@users.noreply.github.com> Date: Fri, 19 Aug 2022 15:34:13 +0200 Subject: [PATCH 06/12] translations(rustc_session): migrate the file cgu_reuse_tracker This commit migrates the errors that indicates an incorrect CGU type and the fatal error that indicates that a CGU has not been correctly recorded --- .../locales/en-US/session.ftl | 5 +++ compiler/rustc_error_messages/src/lib.rs | 1 + .../rustc_session/src/cgu_reuse_tracker.rs | 34 ++++++++++++++++--- compiler/rustc_session/src/errors.rs | 22 ++++++++++++ compiler/rustc_session/src/lib.rs | 3 ++ 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 compiler/rustc_error_messages/locales/en-US/session.ftl create mode 100644 compiler/rustc_session/src/errors.rs diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl new file mode 100644 index 0000000000000..71d3abc5e6b7c --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -0,0 +1,5 @@ +incorrect_cgu_reuse_type = + CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be `{$at_least}``${expected_reuse}` + +cgu_not_recorded = + CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded` \ No newline at end of file diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 3e66d12bb37ec..c9a6f70edc0de 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -37,6 +37,7 @@ fluent_messages! { builtin_macros => "../locales/en-US/builtin_macros.ftl", const_eval => "../locales/en-US/const_eval.ftl", expand => "../locales/en-US/expand.ftl", + session => "../locales/en-US/session.ftl", interface => "../locales/en-US/interface.ftl", lint => "../locales/en-US/lint.ftl", parser => "../locales/en-US/parser.ftl", diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs index dd64e8ab71e7b..88fcbf7c1133a 100644 --- a/compiler/rustc_session/src/cgu_reuse_tracker.rs +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -2,8 +2,13 @@ //! compilation. This is used for incremental compilation tests and debug //! output. +use crate::errors::IncorrectCguReuseType; +// use crate::errors::{CguNotRecorded, IncorrectCguReuseType}; use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_span::{Span, Symbol}; +use std::borrow::Cow; +use std::fmt::{self}; use std::sync::{Arc, Mutex}; use tracing::debug; @@ -14,6 +19,22 @@ pub enum CguReuse { PostLto, } +impl fmt::Display for CguReuse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CguReuse::No => write!(f, "No"), + CguReuse::PreLto => write!(f, "PreLto "), + CguReuse::PostLto => write!(f, "PostLto "), + } + } +} + +impl IntoDiagnosticArg for CguReuse { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + } +} + #[derive(Copy, Clone, Debug, PartialEq)] pub enum ComparisonKind { Exact, @@ -99,11 +120,13 @@ impl CguReuseTracker { if error { let at_least = if at_least { "at least " } else { "" }; - let msg = format!( - "CGU-reuse for `{cgu_user_name}` is `{actual_reuse:?}` but \ - should be {at_least}`{expected_reuse:?}`" - ); - diag.span_err(error_span.0, &msg); + IncorrectCguReuseType { + span: error_span.0, + cgu_user_name: &cgu_user_name, + actual_reuse, + expected_reuse, + at_least, + }; } } else { let msg = format!( @@ -111,6 +134,7 @@ impl CguReuseTracker { not recorded" ); diag.span_fatal(error_span.0, &msg) + // CguNotRecorded { cgu_user_name, cgu_name }; } } } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs new file mode 100644 index 0000000000000..ef78d1c98361b --- /dev/null +++ b/compiler/rustc_session/src/errors.rs @@ -0,0 +1,22 @@ +use crate as rustc_session; +use crate::cgu_reuse_tracker::CguReuse; +use rustc_macros::SessionDiagnostic; +use rustc_span::Span; + +#[derive(SessionDiagnostic)] +#[error(session::incorrect_cgu_reuse_type)] +pub struct IncorrectCguReuseType<'a> { + #[primary_span] + pub span: Span, + pub cgu_user_name: &'a str, + pub actual_reuse: CguReuse, + pub expected_reuse: CguReuse, + pub at_least: &'a str, +} + +// #[derive(SessionDiagnostic)] +// #[fatal(session::cgu_not_recorded)] +// pub struct CguNotRecorded<'a> { +// pub cgu_user_name: &'a str, +// pub cgu_name: &'a str, +// } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 0617bd5fae786..113bd85135eae 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -8,9 +8,12 @@ #![feature(map_many_mut)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate rustc_macros; +pub mod errors; pub mod cgu_reuse_tracker; pub mod utils; From 77b01acab7ac7623c1225021a9bc1a0123c9af81 Mon Sep 17 00:00:00 2001 From: Luis Cardoso <61982523+LuisCardosoOliveira@users.noreply.github.com> Date: Mon, 22 Aug 2022 08:28:50 +0200 Subject: [PATCH 07/12] translations(rustc_session): migrate 80% of the file parse.rs This commit migrates around 80% of the parse file to use SsessionDiagnostic We still have to migrate struct_err and struct_warn. --- .../locales/en-US/session.ftl | 14 ++++++++-- .../rustc_session/src/cgu_reuse_tracker.rs | 4 ++- compiler/rustc_session/src/errors.rs | 28 +++++++++++++++++-- compiler/rustc_session/src/lib.rs | 2 -- compiler/rustc_session/src/parse.rs | 21 ++++++++++---- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index 71d3abc5e6b7c..e94a7b2c1c8e9 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -1,5 +1,13 @@ -incorrect_cgu_reuse_type = +session_incorrect_cgu_reuse_type = CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be `{$at_least}``${expected_reuse}` -cgu_not_recorded = - CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded` \ No newline at end of file +session_cgu_not_recorded = + CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded` + +session_feature_gate_error = {$explain} + +session_feature_diagnostic_for_issue = + see issue #{$n} for more information + +session_feature_diagnostic_help = + add `#![feature({$feature})]` to the crate attributes to enable diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs index 88fcbf7c1133a..0b75a89c4d8a1 100644 --- a/compiler/rustc_session/src/cgu_reuse_tracker.rs +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -3,7 +3,6 @@ //! output. use crate::errors::IncorrectCguReuseType; -// use crate::errors::{CguNotRecorded, IncorrectCguReuseType}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_span::{Span, Symbol}; @@ -129,11 +128,14 @@ impl CguReuseTracker { }; } } else { + //FIXME: Remove this once PR #100694 that implements `[fatal(..)]` is merged let msg = format!( "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \ not recorded" ); diag.span_fatal(error_span.0, &msg) + + //FIXME: Uncomment this once PR #100694 that implements `[fatal(..)]` is merged // CguNotRecorded { cgu_user_name, cgu_name }; } } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index ef78d1c98361b..54e5fe82f5c4d 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -1,10 +1,13 @@ +use std::num::NonZeroU32; + use crate as rustc_session; use crate::cgu_reuse_tracker::CguReuse; +use rustc_errors::MultiSpan; use rustc_macros::SessionDiagnostic; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; #[derive(SessionDiagnostic)] -#[error(session::incorrect_cgu_reuse_type)] +#[diag(session::incorrect_cgu_reuse_type)] pub struct IncorrectCguReuseType<'a> { #[primary_span] pub span: Span, @@ -14,9 +17,30 @@ pub struct IncorrectCguReuseType<'a> { pub at_least: &'a str, } +//FIXME: Uncomment this once PR #100694 that implements `[fatal(..)]` is merged // #[derive(SessionDiagnostic)] // #[fatal(session::cgu_not_recorded)] // pub struct CguNotRecorded<'a> { // pub cgu_user_name: &'a str, // pub cgu_name: &'a str, // } + +#[derive(SessionDiagnostic)] +#[diag(session::feature_gate_error, code = "E0658")] +pub struct FeatureGateError<'a> { + #[primary_span] + pub span: MultiSpan, + pub explain: &'a str, +} + +#[derive(SessionSubdiagnostic)] +#[note(session::feature_diagnostic_for_issue)] +pub struct FeatureDiagnosticForIssue { + pub n: NonZeroU32, +} + +#[derive(SessionSubdiagnostic)] +#[help(session::feature_diagnostic_help)] +pub struct FeatureDiagnosticHelp { + pub feature: Symbol, +} diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 113bd85135eae..3f098078173f4 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -8,8 +8,6 @@ #![feature(map_many_mut)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 17866dc6bddcb..ebec754dcffb8 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -2,6 +2,7 @@ //! It also serves as an input to the parser itself. use crate::config::CheckCfg; +use crate::errors::{FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError}; use crate::lint::{ builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId, }; @@ -11,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{ - error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, + fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, MultiSpan, StashKey, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; @@ -112,7 +113,7 @@ pub fn feature_err_issue<'a>( .map(|err| err.cancel()); } - let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658)); + let mut err = sess.create_err(FeatureGateError { span, explain }); add_feature_diagnostics_for_issue(&mut err, sess, feature, issue); err } @@ -130,6 +131,8 @@ pub fn feature_warn<'a>(sess: &'a ParseSess, feature: Symbol, span: Span, explai /// /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`. +#[allow(rustc::diagnostic_outside_of_impl)] +#[allow(rustc::untranslatable_diagnostic)] pub fn feature_warn_issue<'a>( sess: &'a ParseSess, feature: Symbol, @@ -172,14 +175,12 @@ pub fn add_feature_diagnostics_for_issue<'a>( issue: GateIssue, ) { if let Some(n) = find_feature_issue(feature, issue) { - err.note(&format!( - "see issue #{n} for more information" - )); + err.subdiagnostic(FeatureDiagnosticForIssue { n }); } // #23973: do not suggest `#![feature(...)]` if we are in beta/stable if sess.unstable_features.is_nightly_build() { - err.help(&format!("add `#![feature({feature})]` to the crate attributes to enable")); + err.subdiagnostic(FeatureDiagnosticHelp { feature }); } } @@ -372,6 +373,8 @@ impl ParseSess { } #[rustc_lint_diagnostics] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] pub fn struct_err( &self, msg: impl Into, @@ -380,16 +383,22 @@ impl ParseSess { } #[rustc_lint_diagnostics] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] pub fn struct_warn(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { self.span_diagnostic.struct_warn(msg) } #[rustc_lint_diagnostics] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] pub fn struct_fatal(&self, msg: impl Into) -> DiagnosticBuilder<'_, !> { self.span_diagnostic.struct_fatal(msg) } #[rustc_lint_diagnostics] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] pub fn struct_diagnostic( &self, msg: impl Into, From 36c42fa617ef0a0c824fd6d87e214fed407a66e5 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Tue, 16 Aug 2022 20:23:32 +0200 Subject: [PATCH 08/12] Use `DisplayBuffer` for socket addresses. --- .../src/net/{ip => addr}/display_buffer.rs | 6 +- library/std/src/net/{ip.rs => addr/ip/mod.rs} | 7 +-- library/std/src/net/{ => addr}/ip/tests.rs | 0 library/std/src/net/addr/mod.rs | 4 ++ .../src/net/{addr.rs => addr/socket/mod.rs} | 58 +++++++------------ .../std/src/net/addr/{ => socket}/tests.rs | 0 library/std/src/net/mod.rs | 5 +- 7 files changed, 34 insertions(+), 46 deletions(-) rename library/std/src/net/{ip => addr}/display_buffer.rs (86%) rename library/std/src/net/{ip.rs => addr/ip/mod.rs} (99%) rename library/std/src/net/{ => addr}/ip/tests.rs (100%) create mode 100644 library/std/src/net/addr/mod.rs rename library/std/src/net/{addr.rs => addr/socket/mod.rs} (94%) rename library/std/src/net/addr/{ => socket}/tests.rs (100%) diff --git a/library/std/src/net/ip/display_buffer.rs b/library/std/src/net/addr/display_buffer.rs similarity index 86% rename from library/std/src/net/ip/display_buffer.rs rename to library/std/src/net/addr/display_buffer.rs index bd852d5da8ec5..7aadf06e92fc6 100644 --- a/library/std/src/net/ip/display_buffer.rs +++ b/library/std/src/net/addr/display_buffer.rs @@ -3,12 +3,12 @@ use crate::mem::MaybeUninit; use crate::str; /// Used for slow path in `Display` implementations when alignment is required. -pub struct IpDisplayBuffer { +pub struct DisplayBuffer { buf: [MaybeUninit; SIZE], len: usize, } -impl IpDisplayBuffer { +impl DisplayBuffer { #[inline] pub const fn new() -> Self { Self { buf: MaybeUninit::uninit_array(), len: 0 } @@ -25,7 +25,7 @@ impl IpDisplayBuffer { } } -impl fmt::Write for IpDisplayBuffer { +impl fmt::Write for DisplayBuffer { fn write_str(&mut self, s: &str) -> fmt::Result { let bytes = s.as_bytes(); diff --git a/library/std/src/net/ip.rs b/library/std/src/net/addr/ip/mod.rs similarity index 99% rename from library/std/src/net/ip.rs rename to library/std/src/net/addr/ip/mod.rs index 6004810655ebb..a670f7168334a 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/addr/ip/mod.rs @@ -8,8 +8,7 @@ use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{FromInner, IntoInner}; -mod display_buffer; -use display_buffer::IpDisplayBuffer; +use super::display_buffer::DisplayBuffer; /// An IP address, either IPv4 or IPv6. /// @@ -997,7 +996,7 @@ impl fmt::Display for Ipv4Addr { } else { const LONGEST_IPV4_ADDR: &str = "255.255.255.255"; - let mut buf = IpDisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new(); + let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new(); // Buffer is long enough for the longest possible IPv4 address, so this should never fail. write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); @@ -1844,7 +1843,7 @@ impl fmt::Display for Ipv6Addr { } else { const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; - let mut buf = IpDisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new(); + let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new(); // Buffer is long enough for the longest possible IPv6 address, so this should never fail. write!(buf, "{}", self).unwrap(); diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/addr/ip/tests.rs similarity index 100% rename from library/std/src/net/ip/tests.rs rename to library/std/src/net/addr/ip/tests.rs diff --git a/library/std/src/net/addr/mod.rs b/library/std/src/net/addr/mod.rs new file mode 100644 index 0000000000000..afecab3049848 --- /dev/null +++ b/library/std/src/net/addr/mod.rs @@ -0,0 +1,4 @@ +mod display_buffer; + +pub mod ip; +pub mod socket; diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr/socket/mod.rs similarity index 94% rename from library/std/src/net/addr.rs rename to library/std/src/net/addr/socket/mod.rs index 53fee952a7a7a..33b0dfa03e0ed 100644 --- a/library/std/src/net/addr.rs +++ b/library/std/src/net/addr/socket/mod.rs @@ -2,9 +2,9 @@ mod tests; use crate::cmp::Ordering; -use crate::fmt; +use crate::fmt::{self, Write}; use crate::hash; -use crate::io::{self, Write}; +use crate::io; use crate::iter; use crate::mem; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; @@ -15,6 +15,8 @@ use crate::sys_common::net::LookupHost; use crate::sys_common::{FromInner, IntoInner}; use crate::vec; +use super::display_buffer::DisplayBuffer; + /// An internet socket address, either IPv4 or IPv6. /// /// Internet socket addresses consist of an [IP address], a 16-bit port number, as well @@ -616,25 +618,18 @@ impl fmt::Debug for SocketAddr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddrV4 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Fast path: if there's no alignment stuff, write to the output buffer - // directly + // If there are no alignment requirements, write the socket address directly to `f`. + // Otherwise, write it to a local buffer and then use `f.pad`. if f.precision().is_none() && f.width().is_none() { write!(f, "{}:{}", self.ip(), self.port()) } else { - const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments - + 3 // the separators - + 1 + 5; // the port - let mut buf = [0; IPV4_SOCKET_BUF_LEN]; - let mut buf_slice = &mut buf[..]; - - // Unwrap is fine because writing to a sufficiently-sized - // buffer is infallible - write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap(); - let len = IPV4_SOCKET_BUF_LEN - buf_slice.len(); - - // This unsafe is OK because we know what is being written to the buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - f.pad(buf) + const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536"; + + let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new(); + // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail. + write!(buf, "{}:{}", self.ip(), self.port()).unwrap(); + + f.pad(buf.as_str()) } } } @@ -649,35 +644,26 @@ impl fmt::Debug for SocketAddrV4 { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddrV6 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Fast path: if there's no alignment stuff, write to the output - // buffer directly + // If there are no alignment requirements, write the socket address directly to `f`. + // Otherwise, write it to a local buffer and then use `f.pad`. if f.precision().is_none() && f.width().is_none() { match self.scope_id() { 0 => write!(f, "[{}]:{}", self.ip(), self.port()), scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()), } } else { - const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address - + 7 // The colon separators - + 2 // The brackets - + 1 + 10 // The scope id - + 1 + 5; // The port - - let mut buf = [0; IPV6_SOCKET_BUF_LEN]; - let mut buf_slice = &mut buf[..]; + const LONGEST_IPV6_SOCKET_ADDR: &str = + "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536"; + let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new(); match self.scope_id() { - 0 => write!(buf_slice, "[{}]:{}", self.ip(), self.port()), - scope_id => write!(buf_slice, "[{}%{}]:{}", self.ip(), scope_id, self.port()), + 0 => write!(buf, "[{}]:{}", self.ip(), self.port()), + scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()), } - // Unwrap is fine because writing to a sufficiently-sized - // buffer is infallible + // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail. .unwrap(); - let len = IPV6_SOCKET_BUF_LEN - buf_slice.len(); - // This unsafe is OK because we know what is being written to the buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - f.pad(buf) + f.pad(buf.as_str()) } } } diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/addr/socket/tests.rs similarity index 100% rename from library/std/src/net/addr/tests.rs rename to library/std/src/net/addr/socket/tests.rs diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index e7a40bdaf8e99..c188de7168d69 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -24,9 +24,9 @@ use crate::io::{self, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +pub use self::addr::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; +pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::parser::AddrParseError; #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] @@ -37,7 +37,6 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; pub use self::udp::UdpSocket; mod addr; -mod ip; mod parser; mod tcp; #[cfg(test)] From 63700a8ed15124853f34d2e1c783c51dd69b05ae Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 20 Aug 2022 00:06:47 +0200 Subject: [PATCH 09/12] Add tests for `SockAddr` `Display`. --- library/std/src/net/addr/socket/tests.rs | 69 ++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/library/std/src/net/addr/socket/tests.rs b/library/std/src/net/addr/socket/tests.rs index 585a17451a0b7..15211f81981ba 100644 --- a/library/std/src/net/addr/socket/tests.rs +++ b/library/std/src/net/addr/socket/tests.rs @@ -51,6 +51,75 @@ fn to_socket_addr_string() { // s has been moved into the tsa call } +#[test] +fn ipv4_socket_addr_to_string() { + // Shortest possible IPv4 length. + assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0"); + + // Longest possible IPv4 length. + assert_eq!( + SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(), + "255.255.255.255:65535" + ); + + // Test padding. + assert_eq!( + &format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), + "1.1.1.1:53 " + ); + assert_eq!( + &format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), + " 1.1.1.1:53" + ); +} + +#[test] +fn ipv6_socket_addr_to_string() { + // IPv4-mapped address. + assert_eq!( + SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0) + .to_string(), + "[::ffff:192.0.2.128]:8080" + ); + + // IPv4-compatible address. + assert_eq!( + SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(), + "[::192.0.2.128]:8080" + ); + + // IPv6 address with no zero segments. + assert_eq!( + SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(), + "[8:9:a:b:c:d:e:f]:80" + ); + + // Shortest possible IPv6 length. + assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0"); + + // Longest possible IPv6 length. + assert_eq!( + SocketAddrV6::new( + Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888), + u16::MAX, + u32::MAX, + u32::MAX, + ) + .to_string(), + "[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535" + ); + + // Test padding. + assert_eq!( + &format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), + "[1:2:3:4:5:6:7:8]:9 " + ); + assert_eq!( + &format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), + " [1:2:3:4:5:6:7:8]:9" + ); +} + #[test] fn bind_udp_socket_bad() { // rust-lang/rust#53957: This is a regression test for a parsing problem From 89c74e8e25216d9e18d515cf4792b0f2ee92226c Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 20 Aug 2022 00:24:49 +0200 Subject: [PATCH 10/12] Move `net::parser` into `net::addr` module. --- library/std/src/net/addr/mod.rs | 1 + library/std/src/net/{parser.rs => addr/parser/mod.rs} | 0 library/std/src/net/{ => addr}/parser/tests.rs | 0 library/std/src/net/mod.rs | 5 ++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename library/std/src/net/{parser.rs => addr/parser/mod.rs} (100%) rename library/std/src/net/{ => addr}/parser/tests.rs (100%) diff --git a/library/std/src/net/addr/mod.rs b/library/std/src/net/addr/mod.rs index afecab3049848..da33666a5805a 100644 --- a/library/std/src/net/addr/mod.rs +++ b/library/std/src/net/addr/mod.rs @@ -1,4 +1,5 @@ mod display_buffer; pub mod ip; +pub mod parser; pub mod socket; diff --git a/library/std/src/net/parser.rs b/library/std/src/net/addr/parser/mod.rs similarity index 100% rename from library/std/src/net/parser.rs rename to library/std/src/net/addr/parser/mod.rs diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/addr/parser/tests.rs similarity index 100% rename from library/std/src/net/parser/tests.rs rename to library/std/src/net/addr/parser/tests.rs diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index c188de7168d69..316a456395000 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -26,9 +26,9 @@ use crate::io::{self, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::addr::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +pub use self::addr::parser::AddrParseError; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::parser::AddrParseError; +pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] pub use self::tcp::IntoIncoming; #[stable(feature = "rust1", since = "1.0.0")] @@ -37,7 +37,6 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; pub use self::udp::UdpSocket; mod addr; -mod parser; mod tcp; #[cfg(test)] mod test; From d61ecec44e1249dfa972a6acc8643827bfaa169a Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 24 Aug 2022 13:13:12 +0200 Subject: [PATCH 11/12] Flatten `net` module again. --- library/std/src/net/addr/mod.rs | 5 ----- library/std/src/net/{addr => }/display_buffer.rs | 0 library/std/src/net/{addr/ip/mod.rs => ip_addr.rs} | 0 library/std/src/net/{addr/ip => ip_addr}/tests.rs | 0 library/std/src/net/mod.rs | 11 +++++++---- library/std/src/net/{addr/parser/mod.rs => parser.rs} | 0 library/std/src/net/{addr => }/parser/tests.rs | 0 .../src/net/{addr/socket/mod.rs => socket_addr.rs} | 0 .../std/src/net/{addr/socket => socket_addr}/tests.rs | 0 9 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 library/std/src/net/addr/mod.rs rename library/std/src/net/{addr => }/display_buffer.rs (100%) rename library/std/src/net/{addr/ip/mod.rs => ip_addr.rs} (100%) rename library/std/src/net/{addr/ip => ip_addr}/tests.rs (100%) rename library/std/src/net/{addr/parser/mod.rs => parser.rs} (100%) rename library/std/src/net/{addr => }/parser/tests.rs (100%) rename library/std/src/net/{addr/socket/mod.rs => socket_addr.rs} (100%) rename library/std/src/net/{addr/socket => socket_addr}/tests.rs (100%) diff --git a/library/std/src/net/addr/mod.rs b/library/std/src/net/addr/mod.rs deleted file mode 100644 index da33666a5805a..0000000000000 --- a/library/std/src/net/addr/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod display_buffer; - -pub mod ip; -pub mod parser; -pub mod socket; diff --git a/library/std/src/net/addr/display_buffer.rs b/library/std/src/net/display_buffer.rs similarity index 100% rename from library/std/src/net/addr/display_buffer.rs rename to library/std/src/net/display_buffer.rs diff --git a/library/std/src/net/addr/ip/mod.rs b/library/std/src/net/ip_addr.rs similarity index 100% rename from library/std/src/net/addr/ip/mod.rs rename to library/std/src/net/ip_addr.rs diff --git a/library/std/src/net/addr/ip/tests.rs b/library/std/src/net/ip_addr/tests.rs similarity index 100% rename from library/std/src/net/addr/ip/tests.rs rename to library/std/src/net/ip_addr/tests.rs diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 316a456395000..cc2e0d9742fea 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -24,11 +24,11 @@ use crate::io::{self, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::addr::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; +pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::addr::parser::AddrParseError; +pub use self::parser::AddrParseError; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] pub use self::tcp::IntoIncoming; #[stable(feature = "rust1", since = "1.0.0")] @@ -36,7 +36,10 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; -mod addr; +mod display_buffer; +mod ip_addr; +mod parser; +mod socket_addr; mod tcp; #[cfg(test)] mod test; diff --git a/library/std/src/net/addr/parser/mod.rs b/library/std/src/net/parser.rs similarity index 100% rename from library/std/src/net/addr/parser/mod.rs rename to library/std/src/net/parser.rs diff --git a/library/std/src/net/addr/parser/tests.rs b/library/std/src/net/parser/tests.rs similarity index 100% rename from library/std/src/net/addr/parser/tests.rs rename to library/std/src/net/parser/tests.rs diff --git a/library/std/src/net/addr/socket/mod.rs b/library/std/src/net/socket_addr.rs similarity index 100% rename from library/std/src/net/addr/socket/mod.rs rename to library/std/src/net/socket_addr.rs diff --git a/library/std/src/net/addr/socket/tests.rs b/library/std/src/net/socket_addr/tests.rs similarity index 100% rename from library/std/src/net/addr/socket/tests.rs rename to library/std/src/net/socket_addr/tests.rs From d90f2a02640a2f1ad389cb537633c7111ee82bc3 Mon Sep 17 00:00:00 2001 From: Luis Cardoso <61982523+LuisCardosoOliveira@users.noreply.github.com> Date: Wed, 24 Aug 2022 17:15:08 +0200 Subject: [PATCH 12/12] translations(rustc_session): migrate check_expected_reuse This commit migrates the errors in the function check_expected_reuse to use the new SessionDiagnostic. It also does some small refactor for the IncorrectCguReuseType to include the 'at least' word in the fluent translation file --- compiler/rustc_codegen_ssa/src/back/write.rs | 2 +- .../locales/en-US/session.ftl | 5 ++++- compiler/rustc_session/src/cgu_reuse_tracker.rs | 17 +++++------------ compiler/rustc_session/src/errors.rs | 15 +++++++-------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 2930d09d71f1a..68f3b19b715ad 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1892,7 +1892,7 @@ impl OngoingCodegen { } }); - sess.cgu_reuse_tracker.check_expected_reuse(sess.diagnostic()); + sess.cgu_reuse_tracker.check_expected_reuse(sess); sess.abort_if_errors(); diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index e94a7b2c1c8e9..983e5cee8237d 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -1,5 +1,8 @@ session_incorrect_cgu_reuse_type = - CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be `{$at_least}``${expected_reuse}` + CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least -> + [one] {"at least "} + *[other] {""} + }`{$expected_reuse}` session_cgu_not_recorded = CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded` diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs index 0b75a89c4d8a1..2a4a772f61085 100644 --- a/compiler/rustc_session/src/cgu_reuse_tracker.rs +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -2,7 +2,8 @@ //! compilation. This is used for incremental compilation tests and debug //! output. -use crate::errors::IncorrectCguReuseType; +use crate::errors::{CguNotRecorded, IncorrectCguReuseType}; +use crate::Session; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_span::{Span, Symbol}; @@ -104,7 +105,7 @@ impl CguReuseTracker { } } - pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) { + pub fn check_expected_reuse(&self, sess: &Session) { if let Some(ref data) = self.data { let data = data.lock().unwrap(); @@ -118,7 +119,7 @@ impl CguReuseTracker { }; if error { - let at_least = if at_least { "at least " } else { "" }; + let at_least = if at_least { 1 } else { 0 }; IncorrectCguReuseType { span: error_span.0, cgu_user_name: &cgu_user_name, @@ -128,15 +129,7 @@ impl CguReuseTracker { }; } } else { - //FIXME: Remove this once PR #100694 that implements `[fatal(..)]` is merged - let msg = format!( - "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \ - not recorded" - ); - diag.span_fatal(error_span.0, &msg) - - //FIXME: Uncomment this once PR #100694 that implements `[fatal(..)]` is merged - // CguNotRecorded { cgu_user_name, cgu_name }; + sess.emit_fatal(CguNotRecorded { cgu_user_name, cgu_name }); } } } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 54e5fe82f5c4d..7252f1799dac1 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -14,16 +14,15 @@ pub struct IncorrectCguReuseType<'a> { pub cgu_user_name: &'a str, pub actual_reuse: CguReuse, pub expected_reuse: CguReuse, - pub at_least: &'a str, + pub at_least: u8, } -//FIXME: Uncomment this once PR #100694 that implements `[fatal(..)]` is merged -// #[derive(SessionDiagnostic)] -// #[fatal(session::cgu_not_recorded)] -// pub struct CguNotRecorded<'a> { -// pub cgu_user_name: &'a str, -// pub cgu_name: &'a str, -// } +#[derive(SessionDiagnostic)] +#[diag(session::cgu_not_recorded)] +pub struct CguNotRecorded<'a> { + pub cgu_user_name: &'a str, + pub cgu_name: &'a str, +} #[derive(SessionDiagnostic)] #[diag(session::feature_gate_error, code = "E0658")]