Auto merge of #75383 - Dylan-DPC:rollup-6hi36zn, r=Dylan-DPC
Rollup of 10 pull requests Successful merges: - #75098 (Clippy pointer cast lint experiment) - #75249 (Only add a border for the rust logo) - #75315 (Avoid deleting temporary files on error) - #75316 (Don't try to use wasm intrinsics on vectors) - #75337 (instance: only polymorphize upvar substs) - #75339 (evaluate required_consts when pushing stack frame in Miri engine) - #75363 (Use existing `infcx` when emitting trait impl diagnostic) - #75366 (Add help button) - #75369 (Move to intra-doc links in /library/core/src/borrow.rs) - #75379 (Use intra-doc links in /library/core/src/cmp.rs) Failed merges: r? @ghost
This commit is contained in:
commit
7189ca604a
@ -3367,6 +3367,7 @@ dependencies = [
|
||||
"smallvec 1.4.0",
|
||||
"stable_deref_trait",
|
||||
"stacker",
|
||||
"tempfile",
|
||||
"tracing",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
@ -40,14 +40,12 @@
|
||||
/// provide a reference to related type `T`, it is often better to use
|
||||
/// [`AsRef<T>`] as more types can safely implement it.
|
||||
///
|
||||
/// [`AsRef<T>`]: ../../std/convert/trait.AsRef.html
|
||||
/// [`BorrowMut<T>`]: trait.BorrowMut.html
|
||||
/// [`BorrowMut<T>`]: BorrowMut
|
||||
/// [`Box<T>`]: ../../std/boxed/struct.Box.html
|
||||
/// [`Mutex<T>`]: ../../std/sync/struct.Mutex.html
|
||||
/// [`Rc<T>`]: ../../std/rc/struct.Rc.html
|
||||
/// [`str`]: ../../std/primitive.str.html
|
||||
/// [`String`]: ../../std/string/struct.String.html
|
||||
/// [`borrow`]: #tymethod.borrow
|
||||
/// [`borrow`]: Borrow::borrow
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -152,10 +150,9 @@
|
||||
/// If it wants to allow others access to the underlying `str`, it can do
|
||||
/// that via `AsRef<str>` which doesn’t carry any extra requirements.
|
||||
///
|
||||
/// [`Hash`]: ../../std/hash/trait.Hash.html
|
||||
/// [`Hash`]: crate::hash::Hash
|
||||
/// [`HashMap<K, V>`]: ../../std/collections/struct.HashMap.html
|
||||
/// [`String`]: ../../std/string/struct.String.html
|
||||
/// [`str`]: ../../std/primitive.str.html
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait Borrow<Borrowed: ?Sized> {
|
||||
/// Immutably borrows from an owned value.
|
||||
@ -187,7 +184,7 @@ pub trait Borrow<Borrowed: ?Sized> {
|
||||
/// an underlying type by providing a mutable reference. See [`Borrow<T>`]
|
||||
/// for more information on borrowing as another type.
|
||||
///
|
||||
/// [`Borrow<T>`]: trait.Borrow.html
|
||||
/// [`Borrow<T>`]: Borrow
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> {
|
||||
/// Mutably borrows from an owned value.
|
||||
|
@ -17,14 +17,8 @@
|
||||
//!
|
||||
//! For more details, see the respective documentation of each item in the list.
|
||||
//!
|
||||
//! [`Eq`]: trait.Eq.html
|
||||
//! [`PartialEq`]: trait.PartialEq.html
|
||||
//! [`Ord`]: trait.Ord.html
|
||||
//! [`PartialOrd`]: trait.PartialOrd.html
|
||||
//! [`Ordering`]: enum.Ordering.html
|
||||
//! [`Reverse`]: struct.Reverse.html
|
||||
//! [`max`]: fn.max.html
|
||||
//! [`min`]: fn.min.html
|
||||
//! [`max`]: Ord::max
|
||||
//! [`min`]: Ord::min
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
|
@ -728,20 +728,25 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
// codegen. Note that this has a semantic difference in that the
|
||||
// intrinsic can trap whereas `fptoui` never traps. That difference,
|
||||
// however, is handled by `fptosui_may_trap` above.
|
||||
//
|
||||
// Note that we skip the wasm intrinsics for vector types where `fptoui`
|
||||
// must be used instead.
|
||||
if self.wasm_and_missing_nontrapping_fptoint() {
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let float_width = self.cx.float_width(src_ty);
|
||||
let int_width = self.cx.int_width(dest_ty);
|
||||
let name = match (int_width, float_width) {
|
||||
(32, 32) => Some("llvm.wasm.trunc.unsigned.i32.f32"),
|
||||
(32, 64) => Some("llvm.wasm.trunc.unsigned.i32.f64"),
|
||||
(64, 32) => Some("llvm.wasm.trunc.unsigned.i64.f32"),
|
||||
(64, 64) => Some("llvm.wasm.trunc.unsigned.i64.f64"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(name) = name {
|
||||
let intrinsic = self.get_intrinsic(name);
|
||||
return self.call(intrinsic, &[val], None);
|
||||
if self.cx.type_kind(src_ty) != TypeKind::Vector {
|
||||
let float_width = self.cx.float_width(src_ty);
|
||||
let int_width = self.cx.int_width(dest_ty);
|
||||
let name = match (int_width, float_width) {
|
||||
(32, 32) => Some("llvm.wasm.trunc.unsigned.i32.f32"),
|
||||
(32, 64) => Some("llvm.wasm.trunc.unsigned.i32.f64"),
|
||||
(64, 32) => Some("llvm.wasm.trunc.unsigned.i64.f32"),
|
||||
(64, 64) => Some("llvm.wasm.trunc.unsigned.i64.f64"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(name) = name {
|
||||
let intrinsic = self.get_intrinsic(name);
|
||||
return self.call(intrinsic, &[val], None);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) }
|
||||
@ -750,18 +755,20 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
if self.wasm_and_missing_nontrapping_fptoint() {
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let float_width = self.cx.float_width(src_ty);
|
||||
let int_width = self.cx.int_width(dest_ty);
|
||||
let name = match (int_width, float_width) {
|
||||
(32, 32) => Some("llvm.wasm.trunc.signed.i32.f32"),
|
||||
(32, 64) => Some("llvm.wasm.trunc.signed.i32.f64"),
|
||||
(64, 32) => Some("llvm.wasm.trunc.signed.i64.f32"),
|
||||
(64, 64) => Some("llvm.wasm.trunc.signed.i64.f64"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(name) = name {
|
||||
let intrinsic = self.get_intrinsic(name);
|
||||
return self.call(intrinsic, &[val], None);
|
||||
if self.cx.type_kind(src_ty) != TypeKind::Vector {
|
||||
let float_width = self.cx.float_width(src_ty);
|
||||
let int_width = self.cx.int_width(dest_ty);
|
||||
let name = match (int_width, float_width) {
|
||||
(32, 32) => Some("llvm.wasm.trunc.signed.i32.f32"),
|
||||
(32, 64) => Some("llvm.wasm.trunc.signed.i32.f64"),
|
||||
(64, 32) => Some("llvm.wasm.trunc.signed.i64.f32"),
|
||||
(64, 64) => Some("llvm.wasm.trunc.signed.i64.f64"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(name) = name {
|
||||
let intrinsic = self.get_intrinsic(name);
|
||||
return self.call(intrinsic, &[val], None);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty, UNNAMED) }
|
||||
|
@ -1,4 +1,5 @@
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_fs_util::fix_windows_verbatim_for_gcc;
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib};
|
||||
@ -23,7 +24,7 @@ use super::rpath::{self, RPathConfig};
|
||||
use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME};
|
||||
|
||||
use cc::windows_registry;
|
||||
use tempfile::{Builder as TempFileBuilder, TempDir};
|
||||
use tempfile::Builder as TempFileBuilder;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -70,27 +71,21 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
||||
}
|
||||
});
|
||||
|
||||
let tmpdir = TempFileBuilder::new()
|
||||
.prefix("rustc")
|
||||
.tempdir()
|
||||
.unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err)));
|
||||
|
||||
if outputs.outputs.should_codegen() {
|
||||
let tmpdir = TempFileBuilder::new()
|
||||
.prefix("rustc")
|
||||
.tempdir()
|
||||
.unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err)));
|
||||
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
|
||||
let out_filename = out_filename(sess, crate_type, outputs, crate_name);
|
||||
match crate_type {
|
||||
CrateType::Rlib => {
|
||||
let _timer = sess.timer("link_rlib");
|
||||
link_rlib::<B>(
|
||||
sess,
|
||||
codegen_results,
|
||||
RlibFlavor::Normal,
|
||||
&out_filename,
|
||||
&tmpdir,
|
||||
)
|
||||
.build();
|
||||
link_rlib::<B>(sess, codegen_results, RlibFlavor::Normal, &out_filename, &path)
|
||||
.build();
|
||||
}
|
||||
CrateType::Staticlib => {
|
||||
link_staticlib::<B>(sess, codegen_results, &out_filename, &tmpdir);
|
||||
link_staticlib::<B>(sess, codegen_results, &out_filename, &path);
|
||||
}
|
||||
_ => {
|
||||
link_natively::<B>(
|
||||
@ -98,7 +93,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
||||
crate_type,
|
||||
&out_filename,
|
||||
codegen_results,
|
||||
tmpdir.path(),
|
||||
path.as_ref(),
|
||||
target_cpu,
|
||||
);
|
||||
}
|
||||
@ -107,10 +102,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
||||
sess.parse_sess.span_diagnostic.emit_artifact_notification(&out_filename, "link");
|
||||
}
|
||||
}
|
||||
|
||||
if sess.opts.cg.save_temps {
|
||||
let _ = tmpdir.into_path();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the temporary object file and metadata if we aren't saving temps
|
||||
@ -279,8 +270,8 @@ pub fn each_linked_rlib(
|
||||
/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
|
||||
/// directory being searched for `extern crate` (observing an incomplete file).
|
||||
/// The returned path is the temporary file containing the complete metadata.
|
||||
pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &TempDir) -> PathBuf {
|
||||
let out_filename = tmpdir.path().join(METADATA_FILENAME);
|
||||
pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &MaybeTempDir) -> PathBuf {
|
||||
let out_filename = tmpdir.as_ref().join(METADATA_FILENAME);
|
||||
let result = fs::write(&out_filename, &metadata.raw_data);
|
||||
|
||||
if let Err(e) = result {
|
||||
@ -301,7 +292,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
||||
codegen_results: &CodegenResults,
|
||||
flavor: RlibFlavor,
|
||||
out_filename: &Path,
|
||||
tmpdir: &TempDir,
|
||||
tmpdir: &MaybeTempDir,
|
||||
) -> B {
|
||||
info!("preparing rlib to {:?}", out_filename);
|
||||
let mut ab = <B as ArchiveBuilder>::new(sess, out_filename, None);
|
||||
@ -406,7 +397,7 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
|
||||
sess: &'a Session,
|
||||
codegen_results: &CodegenResults,
|
||||
out_filename: &Path,
|
||||
tempdir: &TempDir,
|
||||
tempdir: &MaybeTempDir,
|
||||
) {
|
||||
let mut ab =
|
||||
link_rlib::<B>(sess, codegen_results, RlibFlavor::StaticlibBase, out_filename, tempdir);
|
||||
|
@ -30,6 +30,7 @@ bitflags = "1.2.1"
|
||||
measureme = "0.7.1"
|
||||
libc = "0.2"
|
||||
stacker = "0.1.9"
|
||||
tempfile = "3.0.5"
|
||||
|
||||
[dependencies.parking_lot]
|
||||
version = "0.10"
|
||||
|
@ -95,6 +95,7 @@ pub mod vec_linked_list;
|
||||
pub mod work_queue;
|
||||
pub use atomic_ref::AtomicRef;
|
||||
pub mod frozen;
|
||||
pub mod temp_dir;
|
||||
|
||||
pub struct OnDrop<F: Fn()>(pub F);
|
||||
|
||||
|
34
src/librustc_data_structures/temp_dir.rs
Normal file
34
src/librustc_data_structures/temp_dir.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
|
||||
/// This is used to avoid TempDir being dropped on error paths unintentionally.
|
||||
#[derive(Debug)]
|
||||
pub struct MaybeTempDir {
|
||||
dir: ManuallyDrop<TempDir>,
|
||||
// Whether the TempDir should be deleted on drop.
|
||||
keep: bool,
|
||||
}
|
||||
|
||||
impl Drop for MaybeTempDir {
|
||||
fn drop(&mut self) {
|
||||
// Safety: We are in the destructor, and no further access will
|
||||
// occur.
|
||||
let dir = unsafe { ManuallyDrop::take(&mut self.dir) };
|
||||
if self.keep {
|
||||
dir.into_path();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for MaybeTempDir {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.dir.path()
|
||||
}
|
||||
}
|
||||
|
||||
impl MaybeTempDir {
|
||||
pub fn new(dir: TempDir, keep_on_drop: bool) -> MaybeTempDir {
|
||||
MaybeTempDir { dir: ManuallyDrop::new(dir), keep: keep_on_drop }
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
use crate::infer::lexical_region_resolve::RegionResolutionError;
|
||||
use crate::infer::{Subtype, TyCtxtInferExt, ValuePairs};
|
||||
use crate::infer::{Subtype, ValuePairs};
|
||||
use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir as hir;
|
||||
@ -53,7 +53,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) {
|
||||
let tcx = self.tcx();
|
||||
let trait_sp = self.tcx().def_span(trait_def_id);
|
||||
let mut err = self
|
||||
.tcx()
|
||||
@ -85,9 +84,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((expected, found)) = tcx
|
||||
.infer_ctxt()
|
||||
.enter(|infcx| infcx.expected_found_str_ty(&ExpectedFound { expected, found }))
|
||||
if let Some((expected, found)) =
|
||||
self.infcx.expected_found_str_ty(&ExpectedFound { expected, found })
|
||||
{
|
||||
// Highlighted the differences when showing the "expected/found" note.
|
||||
err.note_expected_found(&"", expected, &"", found);
|
||||
|
@ -9,6 +9,7 @@ use rustc_ast::{self, ast, visit};
|
||||
use rustc_codegen_ssa::back::link::emit_metadata;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal};
|
||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel};
|
||||
use rustc_errors::{ErrorReported, PResult};
|
||||
use rustc_expand::base::ExtCtxt;
|
||||
@ -974,6 +975,7 @@ fn encode_and_write_metadata(
|
||||
.prefix("rmeta")
|
||||
.tempdir_in(out_filename.parent().unwrap())
|
||||
.unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
|
||||
let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
|
||||
let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir);
|
||||
if let Err(e) = fs::rename(&metadata_filename, &out_filename) {
|
||||
tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
|
||||
|
@ -1,17 +1,13 @@
|
||||
use super::{AllocId, Pointer, RawConst, Scalar};
|
||||
|
||||
use crate::mir::interpret::ConstValue;
|
||||
use crate::ty::layout::LayoutError;
|
||||
use crate::ty::query::TyCtxtAt;
|
||||
use crate::ty::{self, layout, tls, FnSig, Ty};
|
||||
use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
|
||||
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_session::CtfeBacktrace;
|
||||
use rustc_span::{def_id::DefId, Pos, Span};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use std::{any::Any, backtrace::Backtrace, fmt, mem};
|
||||
|
||||
@ -34,167 +30,6 @@ CloneTypeFoldableAndLiftImpls! {
|
||||
pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
|
||||
pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConstEvalErr<'tcx> {
|
||||
pub span: Span,
|
||||
pub error: crate::mir::interpret::InterpError<'tcx>,
|
||||
pub stacktrace: Vec<FrameInfo<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrameInfo<'tcx> {
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
pub span: Span,
|
||||
pub lint_root: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
|
||||
== DefPathData::ClosureExpr
|
||||
{
|
||||
write!(f, "inside closure")?;
|
||||
} else {
|
||||
write!(f, "inside `{}`", self.instance)?;
|
||||
}
|
||||
if !self.span.is_dummy() {
|
||||
let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo());
|
||||
write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEvalErr<'tcx> {
|
||||
pub fn struct_error(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(tcx, message, emit, None)
|
||||
}
|
||||
|
||||
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
|
||||
self.struct_error(tcx, message, |mut e| e.emit())
|
||||
}
|
||||
|
||||
pub fn report_as_lint(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
lint_root: hir::HirId,
|
||||
span: Option<Span>,
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(
|
||||
tcx,
|
||||
message,
|
||||
|mut lint: DiagnosticBuilder<'_>| {
|
||||
// Apply the span.
|
||||
if let Some(span) = span {
|
||||
let primary_spans = lint.span.primary_spans().to_vec();
|
||||
// point at the actual error as the primary span
|
||||
lint.replace_span_with(span);
|
||||
// point to the `const` statement as a secondary span
|
||||
// they don't have any label
|
||||
for sp in primary_spans {
|
||||
if sp != span {
|
||||
lint.span_label(sp, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
lint.emit();
|
||||
},
|
||||
Some(lint_root),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a diagnostic for this const eval error.
|
||||
///
|
||||
/// Sets the message passed in via `message` and adds span labels with detailed error
|
||||
/// information before handing control back to `emit` to do any final processing.
|
||||
/// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
|
||||
/// function to dispose of the diagnostic properly.
|
||||
///
|
||||
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
|
||||
/// (Except that for some errors, we ignore all that -- see `must_error` below.)
|
||||
fn struct_generic(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
lint_root: Option<hir::HirId>,
|
||||
) -> ErrorHandled {
|
||||
let must_error = match self.error {
|
||||
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
|
||||
return ErrorHandled::TooGeneric;
|
||||
}
|
||||
err_inval!(TypeckError(error_reported)) => {
|
||||
return ErrorHandled::Reported(error_reported);
|
||||
}
|
||||
// We must *always* hard error on these, even if the caller wants just a lint.
|
||||
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
|
||||
_ => false,
|
||||
};
|
||||
trace!("reporting const eval failure at {:?}", self.span);
|
||||
|
||||
let err_msg = match &self.error {
|
||||
InterpError::MachineStop(msg) => {
|
||||
// A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
|
||||
// Should be turned into a string by now.
|
||||
msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
|
||||
}
|
||||
err => err.to_string(),
|
||||
};
|
||||
|
||||
let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
|
||||
if let Some(span_msg) = span_msg {
|
||||
err.span_label(self.span, span_msg);
|
||||
}
|
||||
// Add spans for the stacktrace. Don't print a single-line backtrace though.
|
||||
if self.stacktrace.len() > 1 {
|
||||
for frame_info in &self.stacktrace {
|
||||
err.span_label(frame_info.span, frame_info.to_string());
|
||||
}
|
||||
}
|
||||
// Let the caller finish the job.
|
||||
emit(err)
|
||||
};
|
||||
|
||||
if must_error {
|
||||
// The `message` makes little sense here, this is a more serious error than the
|
||||
// caller thinks anyway.
|
||||
// See <https://github.com/rust-lang/rust/pull/63152>.
|
||||
finish(struct_error(tcx, &err_msg), None);
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
} else {
|
||||
// Regular case.
|
||||
if let Some(lint_root) = lint_root {
|
||||
// Report as lint.
|
||||
let hir_id = self
|
||||
.stacktrace
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|frame| frame.lint_root)
|
||||
.unwrap_or(lint_root);
|
||||
tcx.struct_span_lint_hir(
|
||||
rustc_session::lint::builtin::CONST_ERR,
|
||||
hir_id,
|
||||
tcx.span,
|
||||
|lint| finish(lint.build(message), Some(err_msg)),
|
||||
);
|
||||
ErrorHandled::Linted
|
||||
} else {
|
||||
// Report as hard error.
|
||||
finish(struct_error(tcx, message), Some(err_msg));
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
|
||||
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
|
||||
}
|
||||
|
@ -117,9 +117,9 @@ use crate::ty::subst::GenericArgKind;
|
||||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
||||
pub use self::error::{
|
||||
struct_error, CheckInAllocMsg, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled,
|
||||
FrameInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType,
|
||||
ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
struct_error, CheckInAllocMsg, ConstEvalRawResult, ConstEvalResult, ErrorHandled, InterpError,
|
||||
InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, ResourceExhaustionInfo,
|
||||
UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
};
|
||||
|
||||
pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit};
|
||||
|
@ -85,8 +85,6 @@ impl FlagComputation {
|
||||
}
|
||||
|
||||
&ty::Generator(_, ref substs, _) => {
|
||||
self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
|
||||
|
||||
let substs = substs.as_generator();
|
||||
let should_remove_further_specializable =
|
||||
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
||||
@ -109,8 +107,6 @@ impl FlagComputation {
|
||||
}
|
||||
|
||||
&ty::Closure(_, substs) => {
|
||||
self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
|
||||
|
||||
let substs = substs.as_closure();
|
||||
let should_remove_further_specializable =
|
||||
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
||||
@ -196,8 +192,6 @@ impl FlagComputation {
|
||||
}
|
||||
|
||||
&ty::FnDef(_, substs) => {
|
||||
self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
|
||||
|
||||
self.add_substs(substs);
|
||||
}
|
||||
|
||||
|
@ -150,12 +150,6 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
|
||||
self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
|
||||
}
|
||||
|
||||
/// Does this value contain closures, generators or functions such that it may require
|
||||
/// polymorphization?
|
||||
fn may_polymorphize(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::MAY_POLYMORPHIZE)
|
||||
}
|
||||
|
||||
/// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`.
|
||||
fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool {
|
||||
pub struct Visitor<F>(F);
|
||||
|
@ -492,6 +492,20 @@ fn polymorphize<'tcx>(
|
||||
let unused = tcx.unused_generic_params(def_id);
|
||||
debug!("polymorphize: unused={:?}", unused);
|
||||
|
||||
// If this is a closure or generator then we need to handle the case where another closure
|
||||
// from the function is captured as an upvar and hasn't been polymorphized. In this case,
|
||||
// the unpolymorphized upvar closure would result in a polymorphized closure producing
|
||||
// multiple mono items (and eventually symbol clashes).
|
||||
let upvars_ty = if tcx.is_closure(def_id) {
|
||||
Some(substs.as_closure().tupled_upvars_ty())
|
||||
} else if tcx.type_of(def_id).is_generator() {
|
||||
Some(substs.as_generator().tupled_upvars_ty())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let has_upvars = upvars_ty.map(|ty| ty.tuple_fields().count() > 0).unwrap_or(false);
|
||||
debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars);
|
||||
|
||||
struct PolymorphizationFolder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
};
|
||||
@ -512,14 +526,6 @@ fn polymorphize<'tcx>(
|
||||
self.tcx.mk_closure(def_id, polymorphized_substs)
|
||||
}
|
||||
}
|
||||
ty::FnDef(def_id, substs) => {
|
||||
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
|
||||
if substs == polymorphized_substs {
|
||||
ty
|
||||
} else {
|
||||
self.tcx.mk_fn_def(def_id, polymorphized_substs)
|
||||
}
|
||||
}
|
||||
ty::Generator(def_id, substs, movability) => {
|
||||
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
|
||||
if substs == polymorphized_substs {
|
||||
@ -537,24 +543,31 @@ fn polymorphize<'tcx>(
|
||||
let is_unused = unused.contains(param.index).unwrap_or(false);
|
||||
debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
|
||||
match param.kind {
|
||||
// If parameter is a const or type parameter..
|
||||
// Upvar case: If parameter is a type parameter..
|
||||
ty::GenericParamDefKind::Type { .. } if
|
||||
// ..and has upvars..
|
||||
has_upvars &&
|
||||
// ..and this param has the same type as the tupled upvars..
|
||||
upvars_ty == Some(substs[param.index as usize].expect_ty()) => {
|
||||
// ..then double-check that polymorphization marked it used..
|
||||
debug_assert!(!is_unused);
|
||||
// ..and polymorphize any closures/generators captured as upvars.
|
||||
let upvars_ty = upvars_ty.unwrap();
|
||||
let polymorphized_upvars_ty = upvars_ty.fold_with(
|
||||
&mut PolymorphizationFolder { tcx });
|
||||
debug!("polymorphize: polymorphized_upvars_ty={:?}", polymorphized_upvars_ty);
|
||||
ty::GenericArg::from(polymorphized_upvars_ty)
|
||||
},
|
||||
|
||||
// Simple case: If parameter is a const or type parameter..
|
||||
ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
|
||||
// ..and is within range and unused..
|
||||
unused.contains(param.index).unwrap_or(false) =>
|
||||
// ..then use the identity for this parameter.
|
||||
tcx.mk_param_from_def(param),
|
||||
|
||||
// If the parameter does not contain any closures or generators, then use the
|
||||
// substitution directly.
|
||||
_ if !substs.may_polymorphize() => substs[param.index as usize],
|
||||
|
||||
// Otherwise, use the substitution after polymorphizing.
|
||||
_ => {
|
||||
let arg = substs[param.index as usize];
|
||||
let polymorphized_arg = arg.fold_with(&mut PolymorphizationFolder { tcx });
|
||||
debug!("polymorphize: arg={:?} polymorphized_arg={:?}", arg, polymorphized_arg);
|
||||
ty::GenericArg::from(polymorphized_arg)
|
||||
}
|
||||
// Otherwise, use the parameter as before.
|
||||
_ => substs[param.index as usize],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -575,10 +575,6 @@ bitflags! {
|
||||
/// Does this value have parameters/placeholders/inference variables which could be
|
||||
/// replaced later, in a way that would change the results of `impl` specialization?
|
||||
const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
|
||||
|
||||
/// Does this value contain closures, generators or functions such that it may require
|
||||
/// polymorphization?
|
||||
const MAY_POLYMORPHIZE = 1 << 18;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_errors::{DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::ty::ConstInt;
|
||||
use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use super::InterpCx;
|
||||
use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine};
|
||||
use crate::interpret::{
|
||||
struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine,
|
||||
};
|
||||
|
||||
/// The CTFE machine has some custom error kinds.
|
||||
#[derive(Clone, Debug)]
|
||||
@ -48,15 +52,155 @@ impl fmt::Display for ConstEvalErrKind {
|
||||
|
||||
impl Error for ConstEvalErrKind {}
|
||||
|
||||
/// Turn an interpreter error into something to report to the user.
|
||||
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
|
||||
/// Should be called only if the error is actually going to to be reported!
|
||||
pub fn error_to_const_error<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>(
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
span: Option<Span>,
|
||||
) -> ConstEvalErr<'tcx> {
|
||||
error.print_backtrace();
|
||||
let stacktrace = ecx.generate_stacktrace();
|
||||
ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) }
|
||||
/// When const-evaluation errors, this type is constructed with the resulting information,
|
||||
/// and then used to emit the error as a lint or hard error.
|
||||
#[derive(Debug)]
|
||||
pub struct ConstEvalErr<'tcx> {
|
||||
pub span: Span,
|
||||
pub error: InterpError<'tcx>,
|
||||
pub stacktrace: Vec<FrameInfo<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEvalErr<'tcx> {
|
||||
/// Turn an interpreter error into something to report to the user.
|
||||
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
|
||||
/// Should be called only if the error is actually going to to be reported!
|
||||
pub fn new<'mir, M: Machine<'mir, 'tcx>>(
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
span: Option<Span>,
|
||||
) -> ConstEvalErr<'tcx>
|
||||
where
|
||||
'tcx: 'mir,
|
||||
{
|
||||
error.print_backtrace();
|
||||
let stacktrace = ecx.generate_stacktrace();
|
||||
ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) }
|
||||
}
|
||||
|
||||
pub fn struct_error(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(tcx, message, emit, None)
|
||||
}
|
||||
|
||||
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
|
||||
self.struct_error(tcx, message, |mut e| e.emit())
|
||||
}
|
||||
|
||||
pub fn report_as_lint(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
lint_root: hir::HirId,
|
||||
span: Option<Span>,
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(
|
||||
tcx,
|
||||
message,
|
||||
|mut lint: DiagnosticBuilder<'_>| {
|
||||
// Apply the span.
|
||||
if let Some(span) = span {
|
||||
let primary_spans = lint.span.primary_spans().to_vec();
|
||||
// point at the actual error as the primary span
|
||||
lint.replace_span_with(span);
|
||||
// point to the `const` statement as a secondary span
|
||||
// they don't have any label
|
||||
for sp in primary_spans {
|
||||
if sp != span {
|
||||
lint.span_label(sp, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
lint.emit();
|
||||
},
|
||||
Some(lint_root),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a diagnostic for this const eval error.
|
||||
///
|
||||
/// Sets the message passed in via `message` and adds span labels with detailed error
|
||||
/// information before handing control back to `emit` to do any final processing.
|
||||
/// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
|
||||
/// function to dispose of the diagnostic properly.
|
||||
///
|
||||
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
|
||||
/// (Except that for some errors, we ignore all that -- see `must_error` below.)
|
||||
fn struct_generic(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
lint_root: Option<hir::HirId>,
|
||||
) -> ErrorHandled {
|
||||
let must_error = match self.error {
|
||||
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
|
||||
return ErrorHandled::TooGeneric;
|
||||
}
|
||||
err_inval!(TypeckError(error_reported)) => {
|
||||
return ErrorHandled::Reported(error_reported);
|
||||
}
|
||||
// We must *always* hard error on these, even if the caller wants just a lint.
|
||||
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
|
||||
_ => false,
|
||||
};
|
||||
trace!("reporting const eval failure at {:?}", self.span);
|
||||
|
||||
let err_msg = match &self.error {
|
||||
InterpError::MachineStop(msg) => {
|
||||
// A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
|
||||
// Should be turned into a string by now.
|
||||
msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
|
||||
}
|
||||
err => err.to_string(),
|
||||
};
|
||||
|
||||
let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
|
||||
if let Some(span_msg) = span_msg {
|
||||
err.span_label(self.span, span_msg);
|
||||
}
|
||||
// Add spans for the stacktrace. Don't print a single-line backtrace though.
|
||||
if self.stacktrace.len() > 1 {
|
||||
for frame_info in &self.stacktrace {
|
||||
err.span_label(frame_info.span, frame_info.to_string());
|
||||
}
|
||||
}
|
||||
// Let the caller finish the job.
|
||||
emit(err)
|
||||
};
|
||||
|
||||
if must_error {
|
||||
// The `message` makes little sense here, this is a more serious error than the
|
||||
// caller thinks anyway.
|
||||
// See <https://github.com/rust-lang/rust/pull/63152>.
|
||||
finish(struct_error(tcx, &err_msg), None);
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
} else {
|
||||
// Regular case.
|
||||
if let Some(lint_root) = lint_root {
|
||||
// Report as lint.
|
||||
let hir_id = self
|
||||
.stacktrace
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|frame| frame.lint_root)
|
||||
.unwrap_or(lint_root);
|
||||
tcx.struct_span_lint_hir(
|
||||
rustc_session::lint::builtin::CONST_ERR,
|
||||
hir_id,
|
||||
tcx.span,
|
||||
|lint| finish(lint.build(message), Some(err_msg)),
|
||||
);
|
||||
ErrorHandled::Linted
|
||||
} else {
|
||||
// Report as hard error.
|
||||
finish(struct_error(tcx, message), Some(err_msg));
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, MemoryExtra};
|
||||
use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra};
|
||||
use crate::interpret::eval_nullary_intrinsic;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind,
|
||||
InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar,
|
||||
ScalarMaybeUninit, StackPopCleanup,
|
||||
};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{ConstEvalErr, ErrorHandled};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{self, subst::Subst, TyCtxt};
|
||||
use rustc_span::source_map::Span;
|
||||
@ -213,7 +214,7 @@ fn validate_and_turn_into_const<'tcx>(
|
||||
})();
|
||||
|
||||
val.map_err(|error| {
|
||||
let err = error_to_const_error(&ecx, error, None);
|
||||
let err = ConstEvalErr::new(&ecx, error, None);
|
||||
err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
|
||||
diag.note(note_on_undefined_behavior_error());
|
||||
diag.emit();
|
||||
@ -312,7 +313,7 @@ pub fn const_eval_raw_provider<'tcx>(
|
||||
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body))
|
||||
.map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty })
|
||||
.map_err(|error| {
|
||||
let err = error_to_const_error(&ecx, error, None);
|
||||
let err = ConstEvalErr::new(&ecx, error, None);
|
||||
// errors in statics are always emitted as fatal errors
|
||||
if is_static {
|
||||
// Ensure that if the above error was either `TooGeneric` or `Reported`
|
||||
|
@ -1,22 +1,22 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, def::DefKind, def_id::DefId, definitions::DefPathData};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::ich::StableHashingContext;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{
|
||||
sign_extend, truncate, FrameInfo, GlobalId, InterpResult, Pointer, Scalar,
|
||||
sign_extend, truncate, GlobalId, InterpResult, Pointer, Scalar,
|
||||
};
|
||||
use rustc_middle::ty::layout::{self, TyAndLayout};
|
||||
use rustc_middle::ty::{
|
||||
self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
|
||||
};
|
||||
use rustc_span::{source_map::DUMMY_SP, Span};
|
||||
use rustc_span::{source_map::DUMMY_SP, Pos, Span};
|
||||
use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout};
|
||||
|
||||
use super::{
|
||||
@ -88,6 +88,14 @@ pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> {
|
||||
pub loc: Option<mir::Location>,
|
||||
}
|
||||
|
||||
/// What we store about a frame in an interpreter backtrace.
|
||||
#[derive(Debug)]
|
||||
pub struct FrameInfo<'tcx> {
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
pub span: Span,
|
||||
pub lint_root: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these
|
||||
pub enum StackPopCleanup {
|
||||
/// Jump to the next block in the caller, or cause UB if None (that's a function
|
||||
@ -185,6 +193,25 @@ impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
|
||||
== DefPathData::ClosureExpr
|
||||
{
|
||||
write!(f, "inside closure")?;
|
||||
} else {
|
||||
write!(f, "inside `{}`", self.instance)?;
|
||||
}
|
||||
if !self.span.is_dummy() {
|
||||
let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo());
|
||||
write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &TargetDataLayout {
|
||||
@ -625,6 +652,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let frame = M::init_frame_extra(self, pre_frame)?;
|
||||
self.stack_mut().push(frame);
|
||||
|
||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||
for const_ in &body.required_consts {
|
||||
let const_ =
|
||||
self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
|
||||
self.const_to_op(const_, None)?;
|
||||
}
|
||||
|
||||
// Locals are initially uninitialized.
|
||||
let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
|
||||
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
|
||||
|
@ -18,7 +18,7 @@ mod visitor;
|
||||
|
||||
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
|
||||
|
||||
pub use self::eval_context::{Frame, InterpCx, LocalState, LocalValue, StackPopCleanup};
|
||||
pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup};
|
||||
pub use self::intern::{intern_const_alloc_recursive, InternKind};
|
||||
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
|
||||
pub use self::memory::{get_static, AllocCheck, FnVal, Memory, MemoryKind};
|
||||
|
@ -26,7 +26,7 @@ use rustc_span::{def_id::DefId, Span};
|
||||
use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout};
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
use crate::const_eval::error_to_const_error;
|
||||
use crate::const_eval::ConstEvalErr;
|
||||
use crate::interpret::{
|
||||
self, compile_time_machine, truncate, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx,
|
||||
LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy,
|
||||
@ -451,7 +451,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
Ok(op) => Some(op),
|
||||
Err(error) => {
|
||||
let tcx = self.ecx.tcx.at(c.span);
|
||||
let err = error_to_const_error(&self.ecx, error, Some(c.span));
|
||||
let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
|
||||
if let Some(lint_root) = self.lint_root(source_info) {
|
||||
let lint_only = match c.literal.val {
|
||||
// Promoteds must lint and not error as the user didn't ask for them
|
||||
|
@ -147,7 +147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum CastError {
|
||||
pub enum CastError {
|
||||
ErrorReported,
|
||||
|
||||
CastToBool,
|
||||
@ -593,7 +593,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||
/// Checks a cast, and report an error if one exists. In some cases, this
|
||||
/// can return Ok and create type errors in the fcx rather than returning
|
||||
/// directly. coercion-cast is handled in check instead of here.
|
||||
fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
|
||||
pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
|
||||
use rustc_middle::ty::cast::CastTy::*;
|
||||
use rustc_middle::ty::cast::IntTy::*;
|
||||
|
||||
|
@ -67,7 +67,7 @@ type parameter).
|
||||
pub mod _match;
|
||||
mod autoderef;
|
||||
mod callee;
|
||||
mod cast;
|
||||
pub mod cast;
|
||||
mod closure;
|
||||
pub mod coercion;
|
||||
mod compare_method;
|
||||
@ -648,7 +648,7 @@ impl Inherited<'_, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> InheritedBuilder<'tcx> {
|
||||
fn enter<F, R>(&mut self, f: F) -> R
|
||||
pub fn enter<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R,
|
||||
{
|
||||
|
@ -74,11 +74,11 @@ extern crate log;
|
||||
#[macro_use]
|
||||
extern crate rustc_middle;
|
||||
|
||||
// This is used by Clippy.
|
||||
// These are used by Clippy.
|
||||
pub mod check;
|
||||
pub mod expr_use_visitor;
|
||||
|
||||
mod astconv;
|
||||
mod check;
|
||||
mod check_unused;
|
||||
mod coherence;
|
||||
mod collect;
|
||||
|
@ -95,6 +95,7 @@ pub fn render<T: Print, S: Print>(
|
||||
placeholder=\"Click or press ‘S’ to search, ‘?’ for more options…\" \
|
||||
type=\"search\">\
|
||||
</div>\
|
||||
<span class=\"help-button\">?</span>
|
||||
<a id=\"settings-menu\" href=\"{root_path}settings.html\">\
|
||||
<img src=\"{static_root_path}wheel{suffix}.svg\" \
|
||||
width=\"18\" \
|
||||
@ -138,7 +139,7 @@ pub fn render<T: Print, S: Print>(
|
||||
if layout.logo.is_empty() {
|
||||
format!(
|
||||
"<a href='{path}index.html'>\
|
||||
<div class='logo-container'>\
|
||||
<div class='logo-container rust-logo'>\
|
||||
<img src='{static_root_path}rust-logo{suffix}.png' alt='logo'></div></a>",
|
||||
path = p,
|
||||
static_root_path = static_root_path,
|
||||
|
@ -473,7 +473,9 @@ function defocusSearchBar() {
|
||||
}());
|
||||
|
||||
document.addEventListener("click", function(ev) {
|
||||
if (hasClass(ev.target, "collapse-toggle")) {
|
||||
if (hasClass(ev.target, "help-button")) {
|
||||
displayHelp(true, ev);
|
||||
} else if (hasClass(ev.target, "collapse-toggle")) {
|
||||
collapseDocs(ev.target, "toggle");
|
||||
} else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
|
||||
collapseDocs(ev.target.parentNode, "toggle");
|
||||
|
@ -674,7 +674,7 @@ a {
|
||||
}
|
||||
.search-container > div {
|
||||
display: inline-flex;
|
||||
width: calc(100% - 34px);
|
||||
width: calc(100% - 63px);
|
||||
}
|
||||
#crate-search {
|
||||
margin-top: 5px;
|
||||
@ -1250,14 +1250,24 @@ h4 > .notable-traits {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#settings-menu {
|
||||
#settings-menu, .help-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
#settings-menu {
|
||||
right: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#theme-picker, #settings-menu {
|
||||
.help-button {
|
||||
right: 30px;
|
||||
font-family: "Fira Sans",sans-serif;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#theme-picker, #settings-menu, .help-button {
|
||||
padding: 4px;
|
||||
width: 27px;
|
||||
height: 29px;
|
||||
|
@ -62,8 +62,11 @@ pre {
|
||||
background-color: #14191f;
|
||||
}
|
||||
|
||||
.logo-container > img {
|
||||
filter: drop-shadow(0 0 5px #fff);
|
||||
.logo-container.rust-logo > img {
|
||||
filter: drop-shadow(1px 0 0px #fff)
|
||||
drop-shadow(0 1px 0 #fff)
|
||||
drop-shadow(-1px 0 0 #fff)
|
||||
drop-shadow(0 -1px 0 #fff);
|
||||
}
|
||||
|
||||
/* Improve the scrollbar display on firefox */
|
||||
@ -489,7 +492,7 @@ kbd {
|
||||
box-shadow-color: #c6cbd1;
|
||||
}
|
||||
|
||||
#theme-picker, #settings-menu {
|
||||
#theme-picker, #settings-menu, .help-button {
|
||||
border-color: #5c6773;
|
||||
background-color: #0f1419;
|
||||
}
|
||||
@ -499,7 +502,8 @@ kbd {
|
||||
}
|
||||
|
||||
#theme-picker:hover, #theme-picker:focus,
|
||||
#settings-menu:hover, #settings-menu:focus {
|
||||
#settings-menu:hover, #settings-menu:focus,
|
||||
.help-button:hover, .help-button:focus {
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,11 @@ pre {
|
||||
background-color: #505050;
|
||||
}
|
||||
|
||||
.logo-container > img {
|
||||
filter: drop-shadow(0 0 5px #fff);
|
||||
.logo-container.rust-logo > img {
|
||||
filter: drop-shadow(1px 0 0px #fff)
|
||||
drop-shadow(0 1px 0 #fff)
|
||||
drop-shadow(-1px 0 0 #fff)
|
||||
drop-shadow(0 -1px 0 #fff)
|
||||
}
|
||||
|
||||
/* Improve the scrollbar display on firefox */
|
||||
@ -383,13 +386,14 @@ kbd {
|
||||
box-shadow-color: #c6cbd1;
|
||||
}
|
||||
|
||||
#theme-picker, #settings-menu {
|
||||
#theme-picker, #settings-menu, .help-button {
|
||||
border-color: #e0e0e0;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
#theme-picker:hover, #theme-picker:focus,
|
||||
#settings-menu:hover, #settings-menu:focus {
|
||||
#settings-menu:hover, #settings-menu:focus,
|
||||
.help-button:hover, .help-button:focus {
|
||||
border-color: #ffb900;
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,8 @@ pre {
|
||||
scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
|
||||
}
|
||||
|
||||
.logo-container > img {
|
||||
filter: drop-shadow(0 0 5px #aaa);
|
||||
.logo-container.rust-logo > img {
|
||||
/* No need for a border in here! */
|
||||
}
|
||||
|
||||
/* Improve the scrollbar display on webkit-based browsers */
|
||||
@ -377,13 +377,14 @@ kbd {
|
||||
box-shadow-color: #c6cbd1;
|
||||
}
|
||||
|
||||
#theme-picker, #settings-menu {
|
||||
#theme-picker, #settings-menu, .help-button {
|
||||
border-color: #e0e0e0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#theme-picker:hover, #theme-picker:focus,
|
||||
#settings-menu:hover, #settings-menu:focus {
|
||||
#settings-menu:hover, #settings-menu:focus,
|
||||
.help-button:hover, .help-button:focus {
|
||||
border-color: #717171;
|
||||
}
|
||||
|
||||
|
@ -1,52 +0,0 @@
|
||||
// compile-flags:-Zpolymorphize=on -Zprint-mono-items=lazy -Copt-level=1
|
||||
// ignore-tidy-linelength
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
|
||||
// Test that only one copy of `Iter::map` and `iter::repeat` are generated.
|
||||
|
||||
fn unused<T>() -> u64 {
|
||||
42
|
||||
}
|
||||
|
||||
fn foo<T>() {
|
||||
let x = [1, 2, 3, std::mem::size_of::<T>()];
|
||||
x.iter().map(|_| ());
|
||||
}
|
||||
|
||||
//~ MONO_ITEM fn core::iter[0]::adapters[0]::{{impl}}[29]::new[0]<core::slice[0]::Iter[0]<usize>, pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.0[External]
|
||||
//~ MONO_ITEM fn core::iter[0]::traits[0]::iterator[0]::Iterator[0]::map[0]<core::slice[0]::Iter[0]<usize>, (), pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.1[Internal]
|
||||
|
||||
fn bar<T>() {
|
||||
std::iter::repeat(unused::<T>);
|
||||
}
|
||||
|
||||
//~ MONO_ITEM fn core::iter[0]::sources[0]::repeat[0]<fn() -> u64> @@ pr_75255-cgu.1[Internal]
|
||||
|
||||
pub fn dispatch() {
|
||||
foo::<String>();
|
||||
foo::<Vec<String>>();
|
||||
|
||||
bar::<String>();
|
||||
bar::<Vec<String>>();
|
||||
}
|
||||
|
||||
// These are all the items that aren't relevant to the test.
|
||||
//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::mem[0]::size_of[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::add[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::is_null[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::offset[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_add[0]<u8> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_offset[0]<u8> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::non_null[0]::{{impl}}[3]::new_unchecked[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::ptr[0]::null[0]<u8> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::as_ptr[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::iter[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::len[0]<usize> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn pr_75255::dispatch[0] @@ pr_75255-cgu.1[External]
|
||||
//~ MONO_ITEM fn pr_75255::foo[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn pr_75255::foo[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn pr_75255::bar[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
|
||||
//~ MONO_ITEM fn pr_75255::bar[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
|
@ -30,41 +30,41 @@ fn main() -> () {
|
||||
}
|
||||
|
||||
alloc0 (static: FOO, size: 8, align: 4) {
|
||||
╾─alloc21─╼ 03 00 00 00 │ ╾──╼....
|
||||
╾─alloc23─╼ 03 00 00 00 │ ╾──╼....
|
||||
}
|
||||
|
||||
alloc21 (size: 48, align: 4) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼....
|
||||
0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼....
|
||||
0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc19─╼ 03 00 00 00 │ ....*...╾──╼....
|
||||
alloc23 (size: 48, align: 4) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 00 00 00 00 │ ....░░░░╾──╼....
|
||||
0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc13─╼ 02 00 00 00 │ ....░░░░╾──╼....
|
||||
0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc21─╼ 03 00 00 00 │ ....*...╾──╼....
|
||||
}
|
||||
|
||||
alloc4 (size: 0, align: 4) {}
|
||||
alloc8 (size: 0, align: 4) {}
|
||||
|
||||
alloc9 (size: 8, align: 4) {
|
||||
╾─alloc7──╼ ╾─alloc8──╼ │ ╾──╼╾──╼
|
||||
alloc13 (size: 8, align: 4) {
|
||||
╾─alloc11─╼ ╾─alloc12─╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
alloc7 (size: 1, align: 1) {
|
||||
alloc11 (size: 1, align: 1) {
|
||||
05 │ .
|
||||
}
|
||||
|
||||
alloc8 (size: 1, align: 1) {
|
||||
alloc12 (size: 1, align: 1) {
|
||||
06 │ .
|
||||
}
|
||||
|
||||
alloc19 (size: 12, align: 4) {
|
||||
╾─a15+0x3─╼ ╾─alloc16─╼ ╾─a18+0x2─╼ │ ╾──╼╾──╼╾──╼
|
||||
alloc21 (size: 12, align: 4) {
|
||||
╾─a17+0x3─╼ ╾─alloc18─╼ ╾─a20+0x2─╼ │ ╾──╼╾──╼╾──╼
|
||||
}
|
||||
|
||||
alloc15 (size: 4, align: 1) {
|
||||
alloc17 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
||||
alloc16 (size: 1, align: 1) {
|
||||
alloc18 (size: 1, align: 1) {
|
||||
2a │ *
|
||||
}
|
||||
|
||||
alloc18 (size: 4, align: 1) {
|
||||
alloc20 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
@ -30,44 +30,44 @@ fn main() -> () {
|
||||
}
|
||||
|
||||
alloc0 (static: FOO, size: 16, align: 8) {
|
||||
╾───────alloc21───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
╾───────alloc23───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
}
|
||||
|
||||
alloc21 (size: 72, align: 8) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼
|
||||
alloc23 (size: 72, align: 8) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc8────────╼ │ ....░░░░╾──────╼
|
||||
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
|
||||
0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc19───────╼ │ ....*...╾──────╼
|
||||
0x20 │ ╾───────alloc13───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc21───────╼ │ ....*...╾──────╼
|
||||
0x40 │ 03 00 00 00 00 00 00 00 │ ........
|
||||
}
|
||||
|
||||
alloc4 (size: 0, align: 8) {}
|
||||
alloc8 (size: 0, align: 8) {}
|
||||
|
||||
alloc9 (size: 16, align: 8) {
|
||||
╾───────alloc7────────╼ ╾───────alloc8────────╼ │ ╾──────╼╾──────╼
|
||||
alloc13 (size: 16, align: 8) {
|
||||
╾───────alloc11───────╼ ╾───────alloc12───────╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
alloc7 (size: 1, align: 1) {
|
||||
alloc11 (size: 1, align: 1) {
|
||||
05 │ .
|
||||
}
|
||||
|
||||
alloc8 (size: 1, align: 1) {
|
||||
alloc12 (size: 1, align: 1) {
|
||||
06 │ .
|
||||
}
|
||||
|
||||
alloc19 (size: 24, align: 8) {
|
||||
0x00 │ ╾─────alloc15+0x3─────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼
|
||||
0x10 │ ╾─────alloc18+0x2─────╼ │ ╾──────╼
|
||||
alloc21 (size: 24, align: 8) {
|
||||
0x00 │ ╾─────alloc17+0x3─────╼ ╾───────alloc18───────╼ │ ╾──────╼╾──────╼
|
||||
0x10 │ ╾─────alloc20+0x2─────╼ │ ╾──────╼
|
||||
}
|
||||
|
||||
alloc15 (size: 4, align: 1) {
|
||||
alloc17 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
||||
alloc16 (size: 1, align: 1) {
|
||||
alloc18 (size: 1, align: 1) {
|
||||
2a │ *
|
||||
}
|
||||
|
||||
alloc18 (size: 4, align: 1) {
|
||||
alloc20 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
20
src/test/ui/consts/const-eval/erroneous-const.rs
Normal file
20
src/test/ui/consts/const-eval/erroneous-const.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Make sure we error on erroneous consts even if they are unused.
|
||||
#![warn(const_err, unconditional_panic)]
|
||||
|
||||
struct PrintName<T>(T);
|
||||
impl<T> PrintName<T> {
|
||||
const VOID: () = [()][2]; //~WARN any use of this value will cause an error
|
||||
//~^ WARN this operation will panic at runtime
|
||||
}
|
||||
|
||||
const fn no_codegen<T>() {
|
||||
if false { //~ERROR evaluation of constant value failed
|
||||
let _ = PrintName::<T>::VOID;
|
||||
}
|
||||
}
|
||||
|
||||
pub static FOO: () = no_codegen::<i32>(); //~ERROR could not evaluate static initializer
|
||||
|
||||
fn main() {
|
||||
FOO
|
||||
}
|
43
src/test/ui/consts/const-eval/erroneous-const.stderr
Normal file
43
src/test/ui/consts/const-eval/erroneous-const.stderr
Normal file
@ -0,0 +1,43 @@
|
||||
warning: this operation will panic at runtime
|
||||
--> $DIR/erroneous-const.rs:6:22
|
||||
|
|
||||
LL | const VOID: () = [()][2];
|
||||
| ^^^^^^^ index out of bounds: the len is 1 but the index is 2
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/erroneous-const.rs:2:20
|
||||
|
|
||||
LL | #![warn(const_err, unconditional_panic)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: any use of this value will cause an error
|
||||
--> $DIR/erroneous-const.rs:6:22
|
||||
|
|
||||
LL | const VOID: () = [()][2];
|
||||
| -----------------^^^^^^^-
|
||||
| |
|
||||
| index out of bounds: the len is 1 but the index is 2
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/erroneous-const.rs:2:9
|
||||
|
|
||||
LL | #![warn(const_err, unconditional_panic)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/erroneous-const.rs:11:5
|
||||
|
|
||||
LL | / if false {
|
||||
LL | | let _ = PrintName::<T>::VOID;
|
||||
LL | | }
|
||||
| |_____^ referenced constant has errors
|
||||
|
||||
error[E0080]: could not evaluate static initializer
|
||||
--> $DIR/erroneous-const.rs:16:22
|
||||
|
|
||||
LL | pub static FOO: () = no_codegen::<i32>();
|
||||
| ^^^^^^^^^^^^^^^^^^^ referenced constant has errors
|
||||
|
||||
error: aborting due to 2 previous errors; 2 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
28
src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs
Normal file
28
src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// Regression test for issue #74918
|
||||
// Tests that we don't ICE after emitting an error
|
||||
|
||||
struct ChunkingIterator<T, S: 'static + Iterator<Item = T>> {
|
||||
source: S,
|
||||
}
|
||||
|
||||
impl<T, S: Iterator<Item = T>> Iterator for ChunkingIterator<T, S> {
|
||||
type Item = IteratorChunk<T, S>; //~ ERROR missing lifetime
|
||||
|
||||
fn next(&mut self) -> Option<IteratorChunk<T, S>> { //~ ERROR `impl`
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct IteratorChunk<'a, T, S: Iterator<Item = T>> {
|
||||
source: &'a mut S,
|
||||
}
|
||||
|
||||
impl<T, S: Iterator<Item = T>> Iterator for IteratorChunk<'_, T, S> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,30 @@
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/issue-74918-missing-lifetime.rs:9:31
|
||||
|
|
||||
LL | type Item = IteratorChunk<T, S>;
|
||||
| ^ expected named lifetime parameter
|
||||
|
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
LL | type Item<'a> = IteratorChunk<<'a>T, S>;
|
||||
| ^^^^ ^^^^
|
||||
|
||||
error: `impl` item signature doesn't match `trait` item signature
|
||||
--> $DIR/issue-74918-missing-lifetime.rs:11:5
|
||||
|
|
||||
LL | fn next(&mut self) -> Option<IteratorChunk<T, S>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&mut ChunkingIterator<T, S>) -> std::option::Option<IteratorChunk<'_, T, S>>`
|
||||
|
|
||||
::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
||||
|
|
||||
LL | fn next(&mut self) -> Option<Self::Item>;
|
||||
| ----------------------------------------- expected `fn(&mut ChunkingIterator<T, S>) -> std::option::Option<IteratorChunk<'static, _, _>>`
|
||||
|
|
||||
= note: expected `fn(&mut ChunkingIterator<T, S>) -> std::option::Option<IteratorChunk<'static, _, _>>`
|
||||
found `fn(&mut ChunkingIterator<T, S>) -> std::option::Option<IteratorChunk<'_, _, _>>`
|
||||
= help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
|
||||
= help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0106`.
|
24
src/test/ui/mismatched_types/issue-75361-mismatched-impl.rs
Normal file
24
src/test/ui/mismatched_types/issue-75361-mismatched-impl.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// Regresison test for issue #75361
|
||||
// Tests that we don't ICE on mismatched types with inference variables
|
||||
|
||||
|
||||
trait MyTrait {
|
||||
type Item;
|
||||
}
|
||||
|
||||
pub trait Graph {
|
||||
type EdgeType;
|
||||
|
||||
fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType>>;
|
||||
}
|
||||
|
||||
impl<T> Graph for T {
|
||||
type EdgeType = T;
|
||||
|
||||
fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType> + '_> { //~ ERROR `impl`
|
||||
panic!()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,19 @@
|
||||
error: `impl` item signature doesn't match `trait` item signature
|
||||
--> $DIR/issue-75361-mismatched-impl.rs:18:3
|
||||
|
|
||||
LL | fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType>>;
|
||||
| --------------------------------------------------------------------- expected `fn(&T) -> std::boxed::Box<(dyn MyTrait<Item = &_> + 'static)>`
|
||||
...
|
||||
LL | fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType> + '_> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&T) -> std::boxed::Box<dyn MyTrait<Item = &_>>`
|
||||
|
|
||||
= note: expected `fn(&T) -> std::boxed::Box<(dyn MyTrait<Item = &T> + 'static)>`
|
||||
found `fn(&T) -> std::boxed::Box<dyn MyTrait<Item = &T>>`
|
||||
help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
|
||||
--> $DIR/issue-75361-mismatched-impl.rs:12:55
|
||||
|
|
||||
LL | fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType>>;
|
||||
| ^^^^^^^^^^^^^^ consider borrowing this type parameter in the trait
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1730,6 +1730,7 @@ Released 2018-09-13
|
||||
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
|
||||
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
|
||||
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
|
||||
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
|
||||
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
|
||||
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
|
||||
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
|
||||
|
@ -788,6 +788,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&to_digit_is_some::TO_DIGIT_IS_SOME,
|
||||
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
|
||||
&transmute::CROSSPOINTER_TRANSMUTE,
|
||||
&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
&transmute::TRANSMUTE_BYTES_TO_STR,
|
||||
&transmute::TRANSMUTE_FLOAT_TO_INT,
|
||||
&transmute::TRANSMUTE_INT_TO_BOOL,
|
||||
@ -1417,6 +1418,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
|
||||
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
|
||||
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
|
||||
LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
|
||||
LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
|
||||
LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT),
|
||||
LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL),
|
||||
@ -1617,6 +1619,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&swap::MANUAL_SWAP),
|
||||
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
|
||||
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
|
||||
LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
|
||||
LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
|
||||
LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT),
|
||||
LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL),
|
||||
|
@ -7,8 +7,10 @@ use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, cast::CastKind, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -48,6 +50,29 @@ declare_clippy_lint! {
|
||||
"transmutes that have the same to and from types or could be a cast/coercion"
|
||||
}
|
||||
|
||||
// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**Checks for transmutes that could be a pointer cast.
|
||||
///
|
||||
/// **Why is this bad?** Readability. The code tricks people into thinking that
|
||||
/// something complex is going on.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// core::intrinsics::transmute::<*const [i32], *const [u16]>(p)
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// p as *const [u16]
|
||||
/// ```
|
||||
pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
complexity,
|
||||
"transmutes that could be a pointer cast"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for transmutes between a type `T` and `*T`.
|
||||
///
|
||||
@ -269,6 +294,7 @@ declare_clippy_lint! {
|
||||
correctness,
|
||||
"transmute between collections of layout-incompatible types"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Transmute => [
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
@ -281,6 +307,7 @@ declare_lint_pass!(Transmute => [
|
||||
TRANSMUTE_INT_TO_FLOAT,
|
||||
TRANSMUTE_FLOAT_TO_INT,
|
||||
UNSOUND_COLLECTION_TRANSMUTE,
|
||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
]);
|
||||
|
||||
// used to check for UNSOUND_COLLECTION_TRANSMUTE
|
||||
@ -601,7 +628,25 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
(_, _) if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from `{}` to `{}` which could be expressed as a pointer cast instead",
|
||||
from_ty,
|
||||
to_ty
|
||||
),
|
||||
|diag| {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
let sugg = arg.as_ty(&to_ty.to_string()).to_string();
|
||||
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
}
|
||||
),
|
||||
_ => {
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -648,3 +693,59 @@ fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the type conversion can be expressed as a pointer cast, instead of
|
||||
/// a transmute. In certain cases, including some invalid casts from array
|
||||
/// references to pointers, this may cause additional errors to be emitted and/or
|
||||
/// ICE error messages. This function will panic if that occurs.
|
||||
fn can_be_expressed_as_pointer_cast<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
use CastKind::*;
|
||||
matches!(
|
||||
check_cast(cx, e, from_ty, to_ty),
|
||||
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
|
||||
)
|
||||
}
|
||||
|
||||
/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of
|
||||
/// the cast. In certain cases, including some invalid casts from array references
|
||||
/// to pointers, this may cause additional errors to be emitted and/or ICE error
|
||||
/// messages. This function will panic if that occurs.
|
||||
fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
|
||||
let hir_id = e.hir_id;
|
||||
let local_def_id = hir_id.owner;
|
||||
|
||||
Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
|
||||
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
|
||||
|
||||
// If we already have errors, we can't be sure we can pointer cast.
|
||||
assert!(
|
||||
!fn_ctxt.errors_reported_since_creation(),
|
||||
"Newly created FnCtxt contained errors"
|
||||
);
|
||||
|
||||
if let Ok(check) = CastCheck::new(
|
||||
&fn_ctxt, e, from_ty, to_ty,
|
||||
// We won't show any error to the user, so we don't care what the span is here.
|
||||
DUMMY_SP, DUMMY_SP,
|
||||
) {
|
||||
let res = check.do_check(&fn_ctxt);
|
||||
|
||||
// do_check's documentation says that it might return Ok and create
|
||||
// errors in the fcx instead of returing Err in some cases. Those cases
|
||||
// should be filtered out before getting here.
|
||||
assert!(
|
||||
!fn_ctxt.errors_reported_since_creation(),
|
||||
"`fn_ctxt` contained errors after cast check!"
|
||||
);
|
||||
|
||||
res.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2215,6 +2215,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "transmute",
|
||||
},
|
||||
Lint {
|
||||
name: "transmutes_expressible_as_ptr_casts",
|
||||
group: "complexity",
|
||||
desc: "transmutes that could be a pointer cast",
|
||||
deprecation: None,
|
||||
module: "transmute",
|
||||
},
|
||||
Lint {
|
||||
name: "transmuting_null",
|
||||
group: "correctness",
|
||||
|
@ -0,0 +1,90 @@
|
||||
// run-rustfix
|
||||
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
|
||||
// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
|
||||
// would otherwise be responsible for
|
||||
#![warn(clippy::useless_transmute)]
|
||||
#![warn(clippy::transmute_ptr_to_ptr)]
|
||||
#![allow(unused_unsafe)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::mem::{size_of, transmute};
|
||||
|
||||
// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
|
||||
// valid, which we quote from below.
|
||||
fn main() {
|
||||
// We should see an error message for each transmute, and no error messages for
|
||||
// the casts, since the casts are the recommended fixes.
|
||||
|
||||
// e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
|
||||
let _ptr_i32_transmute = unsafe {
|
||||
usize::MAX as *const i32
|
||||
};
|
||||
let ptr_i32 = usize::MAX as *const i32;
|
||||
|
||||
// e has type *T, U is *U_0, and either U_0: Sized ...
|
||||
let _ptr_i8_transmute = unsafe {
|
||||
ptr_i32 as *const i8
|
||||
};
|
||||
let _ptr_i8 = ptr_i32 as *const i8;
|
||||
|
||||
let slice_ptr = &[0,1,2,3] as *const [i32];
|
||||
|
||||
// ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
|
||||
let _ptr_to_unsized_transmute = unsafe {
|
||||
slice_ptr as *const [u16]
|
||||
};
|
||||
let _ptr_to_unsized = slice_ptr as *const [u16];
|
||||
// TODO: We could try testing vtable casts here too, but maybe
|
||||
// we should wait until std::raw::TraitObject is stabilized?
|
||||
|
||||
// e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
|
||||
let _usize_from_int_ptr_transmute = unsafe {
|
||||
ptr_i32 as usize
|
||||
};
|
||||
let _usize_from_int_ptr = ptr_i32 as usize;
|
||||
|
||||
let array_ref: &[i32; 4] = &[1,2,3,4];
|
||||
|
||||
// e has type &[T; n] and U is *const T; array-ptr-cast
|
||||
let _array_ptr_transmute = unsafe {
|
||||
array_ref as *const [i32; 4]
|
||||
};
|
||||
let _array_ptr = array_ref as *const [i32; 4];
|
||||
|
||||
fn foo(_: usize) -> u8 { 42 }
|
||||
|
||||
// e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
|
||||
let _usize_ptr_transmute = unsafe {
|
||||
foo as *const usize
|
||||
};
|
||||
let _usize_ptr_transmute = foo as *const usize;
|
||||
|
||||
// e is a function pointer type and U is an integer; fptr-addr-cast
|
||||
let _usize_from_fn_ptr_transmute = unsafe {
|
||||
foo as usize
|
||||
};
|
||||
let _usize_from_fn_ptr = foo as *const usize;
|
||||
}
|
||||
|
||||
// If a ref-to-ptr cast of this form where the pointer type points to a type other
|
||||
// than the referenced type, calling `CastCheck::do_check` has been observed to
|
||||
// cause an ICE error message. `do_check` is currently called inside the
|
||||
// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
|
||||
// currently prevent it from being called in these cases. This test is meant to
|
||||
// fail if the ordering of the checks ever changes enough to cause these cases to
|
||||
// fall through into `do_check`.
|
||||
fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
|
||||
unsafe { in_param as *const [i32; 1] as *const u8 }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Single(u64);
|
||||
|
||||
#[repr(C)]
|
||||
struct Pair(u32, u32);
|
||||
|
||||
fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
|
||||
assert_eq!(size_of::<Single>(), size_of::<Pair>());
|
||||
|
||||
unsafe { transmute::<Single, Pair>(in_param) }
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
// run-rustfix
|
||||
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
|
||||
// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
|
||||
// would otherwise be responsible for
|
||||
#![warn(clippy::useless_transmute)]
|
||||
#![warn(clippy::transmute_ptr_to_ptr)]
|
||||
#![allow(unused_unsafe)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::mem::{size_of, transmute};
|
||||
|
||||
// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
|
||||
// valid, which we quote from below.
|
||||
fn main() {
|
||||
// We should see an error message for each transmute, and no error messages for
|
||||
// the casts, since the casts are the recommended fixes.
|
||||
|
||||
// e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
|
||||
let _ptr_i32_transmute = unsafe {
|
||||
transmute::<usize, *const i32>(usize::MAX)
|
||||
};
|
||||
let ptr_i32 = usize::MAX as *const i32;
|
||||
|
||||
// e has type *T, U is *U_0, and either U_0: Sized ...
|
||||
let _ptr_i8_transmute = unsafe {
|
||||
transmute::<*const i32, *const i8>(ptr_i32)
|
||||
};
|
||||
let _ptr_i8 = ptr_i32 as *const i8;
|
||||
|
||||
let slice_ptr = &[0,1,2,3] as *const [i32];
|
||||
|
||||
// ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
|
||||
let _ptr_to_unsized_transmute = unsafe {
|
||||
transmute::<*const [i32], *const [u16]>(slice_ptr)
|
||||
};
|
||||
let _ptr_to_unsized = slice_ptr as *const [u16];
|
||||
// TODO: We could try testing vtable casts here too, but maybe
|
||||
// we should wait until std::raw::TraitObject is stabilized?
|
||||
|
||||
// e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
|
||||
let _usize_from_int_ptr_transmute = unsafe {
|
||||
transmute::<*const i32, usize>(ptr_i32)
|
||||
};
|
||||
let _usize_from_int_ptr = ptr_i32 as usize;
|
||||
|
||||
let array_ref: &[i32; 4] = &[1,2,3,4];
|
||||
|
||||
// e has type &[T; n] and U is *const T; array-ptr-cast
|
||||
let _array_ptr_transmute = unsafe {
|
||||
transmute::<&[i32; 4], *const [i32; 4]>(array_ref)
|
||||
};
|
||||
let _array_ptr = array_ref as *const [i32; 4];
|
||||
|
||||
fn foo(_: usize) -> u8 { 42 }
|
||||
|
||||
// e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
|
||||
let _usize_ptr_transmute = unsafe {
|
||||
transmute::<fn(usize) -> u8, *const usize>(foo)
|
||||
};
|
||||
let _usize_ptr_transmute = foo as *const usize;
|
||||
|
||||
// e is a function pointer type and U is an integer; fptr-addr-cast
|
||||
let _usize_from_fn_ptr_transmute = unsafe {
|
||||
transmute::<fn(usize) -> u8, usize>(foo)
|
||||
};
|
||||
let _usize_from_fn_ptr = foo as *const usize;
|
||||
}
|
||||
|
||||
// If a ref-to-ptr cast of this form where the pointer type points to a type other
|
||||
// than the referenced type, calling `CastCheck::do_check` has been observed to
|
||||
// cause an ICE error message. `do_check` is currently called inside the
|
||||
// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
|
||||
// currently prevent it from being called in these cases. This test is meant to
|
||||
// fail if the ordering of the checks ever changes enough to cause these cases to
|
||||
// fall through into `do_check`.
|
||||
fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
|
||||
unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Single(u64);
|
||||
|
||||
#[repr(C)]
|
||||
struct Pair(u32, u32);
|
||||
|
||||
fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
|
||||
assert_eq!(size_of::<Single>(), size_of::<Pair>());
|
||||
|
||||
unsafe { transmute::<Single, Pair>(in_param) }
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
error: transmute from an integer to a pointer
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:20:9
|
||||
|
|
||||
LL | transmute::<usize, *const i32>(usize::MAX)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
|
||||
|
|
||||
= note: `-D clippy::useless-transmute` implied by `-D warnings`
|
||||
|
||||
error: transmute from a pointer to a pointer
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:26:9
|
||||
|
|
||||
LL | transmute::<*const i32, *const i8>(ptr_i32)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
|
||||
|
|
||||
= note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
|
||||
|
||||
error: transmute from a pointer to a pointer
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:34:9
|
||||
|
|
||||
LL | transmute::<*const [i32], *const [u16]>(slice_ptr)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]`
|
||||
|
||||
error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:42:9
|
||||
|
|
||||
LL | transmute::<*const i32, usize>(ptr_i32)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
|
||||
|
|
||||
= note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings`
|
||||
|
||||
error: transmute from a reference to a pointer
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:50:9
|
||||
|
|
||||
LL | transmute::<&[i32; 4], *const [i32; 4]>(array_ref)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
|
||||
|
||||
error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:58:9
|
||||
|
|
||||
LL | transmute::<fn(usize) -> u8, *const usize>(foo)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
|
||||
|
||||
error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:64:9
|
||||
|
|
||||
LL | transmute::<fn(usize) -> u8, usize>(foo)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: transmute from a reference to a pointer
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:77:14
|
||||
|
|
||||
LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user