[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
- branch = rustc/11.0-2020-10-12
+ branch = rustc/11.0-2021-01-05
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book
url = https://github.com/rust-embedded/book.git
name = "rustc_driver"
version = "0.0.0"
dependencies = [
+ "atty",
"libc",
"rustc_ast",
"rustc_ast_pretty",
use tracing::debug;
macro_rules! gate_feature_fn {
+ ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
+ let (visitor, has_feature, span, name, explain, help) =
+ (&*$visitor, $has_feature, $span, $name, $explain, $help);
+ let has_feature: bool = has_feature(visitor.features);
+ debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
+ if !has_feature && !span.allows_unstable($name) {
+ feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
+ .help(help)
+ .emit();
+ }
+ }};
($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
let (visitor, has_feature, span, name, explain) =
(&*$visitor, $has_feature, $span, $name, $explain);
}
macro_rules! gate_feature_post {
+ ($visitor: expr, $feature: ident, $span: expr, $explain: expr, $help: expr) => {
+ gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain, $help)
+ };
($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
};
let spans = sess.parse_sess.gated_spans.spans.borrow();
macro_rules! gate_all {
+ ($gate:ident, $msg:literal, $help:literal) => {
+ if let Some(spans) = spans.get(&sym::$gate) {
+ for span in spans {
+ gate_feature_post!(&visitor, $gate, *span, $msg, $help);
+ }
+ }
+ };
($gate:ident, $msg:literal) => {
if let Some(spans) = spans.get(&sym::$gate) {
for span in spans {
}
gate_all!(if_let_guard, "`if let` guards are experimental");
gate_all!(let_chains, "`let` expressions in this position are experimental");
- gate_all!(async_closure, "async closures are unstable");
+ gate_all!(
+ async_closure,
+ "async closures are unstable",
+ "to use an async block, remove the `||`: `async {`"
+ );
gate_all!(generators, "yield syntax is experimental");
gate_all!(or_patterns, "or-patterns syntax is experimental");
gate_all!(raw_ref_op, "raw address of syntax is experimental");
use crate::deriving::*;
-use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtension, SyntaxExtensionKind};
+use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
use rustc_expand::proc_macro::BangProcMacro;
-use rustc_span::edition::Edition;
-use rustc_span::symbol::{sym, Ident};
+use rustc_span::symbol::sym;
mod asm;
mod assert;
pub mod standard_library_imports;
pub mod test_harness;
-pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand, edition: Edition) {
- let mut register = |name, kind| {
- resolver.register_builtin_macro(
- Ident::with_dummy_span(name),
- SyntaxExtension::default(kind, edition),
- )
- };
+pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
+ let mut register = |name, kind| resolver.register_builtin_macro(name, kind);
macro register_bang($($name:ident: $f:expr,)*) {
$(register(sym::$name, SyntaxExtensionKind::LegacyBang(Box::new($f as MacroExpanderFn)));)*
}
if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) {
let def_ids = unreachable_def_ids_by_file
.entry(*non_codegenned_file_name)
- .or_insert_with(|| Vec::new());
+ .or_insert_with(Vec::new);
def_ids.push(non_codegenned_def_id);
}
}
fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) {
+ (CrateType::Executable, _, _) if sess.is_wasi_reactor() => LinkOutputKind::WasiReactorExe,
(CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe,
(CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe,
(CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe,
self.cmd.arg("-static");
self.build_dylib(out_filename);
}
+ LinkOutputKind::WasiReactorExe => {
+ self.linker_arg("--entry");
+ self.linker_arg("_initialize");
+ }
}
// VxWorks compiler driver introduced `--static-crt` flag specifically for rustc,
// it switches linking for libc and similar system libraries to static without using
arg.push(out_filename.with_extension("dll.lib"));
self.cmd.arg(arg);
}
+ LinkOutputKind::WasiReactorExe => {
+ panic!("can't link as reactor on non-wasi target");
+ }
}
}
LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
self.cmd.arg("--no-entry");
}
+ LinkOutputKind::WasiReactorExe => {
+ self.cmd.arg("--entry");
+ self.cmd.arg("_initialize");
+ }
}
}
// `expression_index`s lower than the referencing `Expression`. Therefore, it is
// reasonable to look up the new index of an expression operand while the `new_indexes`
// vector is only complete up to the current `ExpressionIndex`.
- let id_to_counter =
- |new_indexes: &IndexVec<InjectedExpressionIndex, Option<MappedExpressionIndex>>,
- id: ExpressionOperandId| {
- if id == ExpressionOperandId::ZERO {
- Some(Counter::zero())
- } else if id.index() < self.counters.len() {
- // Note: Some codegen-injected Counters may be only referenced by `Expression`s,
- // and may not have their own `CodeRegion`s,
- let index = CounterValueReference::from(id.index());
- Some(Counter::counter_value_reference(index))
- } else {
- let index = self.expression_index(u32::from(id));
- self.expressions
- .get(index)
- .expect("expression id is out of range")
- .as_ref()
- // If an expression was optimized out, assume it would have produced a count
- // of zero. This ensures that expressions dependent on optimized-out
- // expressions are still valid.
- .map_or(Some(Counter::zero()), |_| {
- new_indexes[index].map(|new_index| Counter::expression(new_index))
- })
- }
- };
+ let id_to_counter = |new_indexes: &IndexVec<
+ InjectedExpressionIndex,
+ Option<MappedExpressionIndex>,
+ >,
+ id: ExpressionOperandId| {
+ if id == ExpressionOperandId::ZERO {
+ Some(Counter::zero())
+ } else if id.index() < self.counters.len() {
+ // Note: Some codegen-injected Counters may be only referenced by `Expression`s,
+ // and may not have their own `CodeRegion`s,
+ let index = CounterValueReference::from(id.index());
+ Some(Counter::counter_value_reference(index))
+ } else {
+ let index = self.expression_index(u32::from(id));
+ self.expressions
+ .get(index)
+ .expect("expression id is out of range")
+ .as_ref()
+ // If an expression was optimized out, assume it would have produced a count
+ // of zero. This ensures that expressions dependent on optimized-out
+ // expressions are still valid.
+ .map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression))
+ }
+ };
for (original_index, expression) in
self.expressions.iter_enumerated().filter_map(|(original_index, entry)| {
use crate::stable_hasher;
use rustc_serialize::{
- opaque::{self, EncodeResult},
+ opaque::{self, EncodeResult, FileEncodeResult},
Decodable, Encodable,
};
use std::hash::{Hash, Hasher};
format!("{:x}{:x}", self.0, self.1)
}
- pub fn encode_opaque(&self, encoder: &mut opaque::Encoder) -> EncodeResult {
- let bytes: [u8; 16] = unsafe { mem::transmute([self.0.to_le(), self.1.to_le()]) };
-
- encoder.emit_raw_bytes(&bytes);
- Ok(())
- }
-
pub fn decode_opaque(decoder: &mut opaque::Decoder<'_>) -> Result<Fingerprint, String> {
let mut bytes: [MaybeUninit<u8>; 16] = MaybeUninit::uninit_array();
impl FingerprintEncoder for opaque::Encoder {
fn encode_fingerprint(&mut self, f: &Fingerprint) -> EncodeResult {
- f.encode_opaque(self)
+ let bytes: [u8; 16] = unsafe { mem::transmute([f.0.to_le(), f.1.to_le()]) };
+ self.emit_raw_bytes(&bytes);
+ Ok(())
+ }
+}
+
+impl FingerprintEncoder for opaque::FileEncoder {
+ fn encode_fingerprint(&mut self, f: &Fingerprint) -> FileEncodeResult {
+ let bytes: [u8; 16] = unsafe { mem::transmute([f.0.to_le(), f.1.to_le()]) };
+ self.emit_raw_bytes(&bytes)
}
}
impl<D: rustc_serialize::Decoder> Decodable<D> for PackedFingerprint {
#[inline]
fn decode(d: &mut D) -> Result<Self, D::Error> {
- Fingerprint::decode(d).map(|f| PackedFingerprint(f))
+ Fingerprint::decode(d).map(PackedFingerprint)
}
}
[dependencies]
libc = "0.2"
+atty = "0.2"
tracing = { version = "0.1.18" }
tracing-subscriber = { version = "0.2.13", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
tracing-tree = "0.1.6"
#[derive(Copy, Clone)]
pub struct RustcDefaultCalls;
-// FIXME remove these and use winapi 0.3 instead
-// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs
-#[cfg(unix)]
-fn stdout_isatty() -> bool {
- unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
-}
-
-#[cfg(windows)]
fn stdout_isatty() -> bool {
- use winapi::um::consoleapi::GetConsoleMode;
- use winapi::um::processenv::GetStdHandle;
- use winapi::um::winbase::STD_OUTPUT_HANDLE;
-
- unsafe {
- let handle = GetStdHandle(STD_OUTPUT_HANDLE);
- let mut out = 0;
- GetConsoleMode(handle, &mut out) != 0
- }
+ atty::is(atty::Stream::Stdout)
}
-// FIXME remove these and use winapi 0.3 instead
-#[cfg(unix)]
-fn stderr_isatty() -> bool {
- unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
-}
-
-#[cfg(windows)]
fn stderr_isatty() -> bool {
- use winapi::um::consoleapi::GetConsoleMode;
- use winapi::um::processenv::GetStdHandle;
- use winapi::um::winbase::STD_ERROR_HANDLE;
-
- unsafe {
- let handle = GetStdHandle(STD_ERROR_HANDLE);
- let mut out = 0;
- GetConsoleMode(handle, &mut out) != 0
- }
+ atty::is(atty::Stream::Stderr)
}
fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
let src_name = input.source_name();
- let src =
- String::clone(&sess.source_map().get_source_file(&src_name).unwrap().src.as_ref().unwrap());
+ let src = String::clone(
+ &sess
+ .source_map()
+ .get_source_file(&src_name)
+ .expect("get_source_file")
+ .src
+ .as_ref()
+ .expect("src"),
+ );
(src, src_name)
}
code_offset: usize,
margin: Margin,
) {
+ // Tabs are assumed to have been replaced by spaces in calling code.
+ assert!(!source_string.contains('\t'));
let line_len = source_string.len();
// Create the source line we will highlight.
let left = margin.left(line_len);
}
let source_string = match file.get_line(line.line_index - 1) {
- Some(s) => s,
+ Some(s) => replace_tabs(&*s),
None => return Vec::new(),
};
let file = annotated_file.file.clone();
let line = &annotated_file.lines[line_idx];
if let Some(source_string) = file.get_line(line.line_index - 1) {
- let leading_whitespace =
- source_string.chars().take_while(|c| c.is_whitespace()).count();
+ let leading_whitespace = source_string
+ .chars()
+ .take_while(|c| c.is_whitespace())
+ .map(|c| {
+ match c {
+ // Tabs are displayed as 4 spaces
+ '\t' => 4,
+ _ => 1,
+ }
+ })
+ .sum();
if source_string.chars().any(|c| !c.is_whitespace()) {
whitespace_margin = min(whitespace_margin, leading_whitespace);
}
self.draw_line(
&mut buffer,
- &unannotated_line,
+ &replace_tabs(&unannotated_line),
annotated_file.lines[line_idx + 1].line_index - 1,
last_buffer_line_num,
width_offset,
);
// print the suggestion
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
- buffer.append(row_num, line, Style::NoStyle);
+ buffer.append(row_num, &replace_tabs(line), Style::NoStyle);
row_num += 1;
}
}
}
+fn replace_tabs(str: &str) -> String {
+ str.replace('\t', " ")
+}
+
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
buffer.puts(line, col, "| ", Style::LineNumber);
}
StyledBuffer { text: vec![], styles: vec![] }
}
- fn replace_tabs(&mut self) {
- for (line_pos, line) in self.text.iter_mut().enumerate() {
- let mut tab_pos = vec![];
- for (pos, c) in line.iter().enumerate() {
- if *c == '\t' {
- tab_pos.push(pos);
- }
- }
- // start with the tabs at the end of the line to replace them with 4 space chars
- for pos in tab_pos.iter().rev() {
- assert_eq!(line.remove(*pos), '\t');
- // fix the position of the style to match up after replacing the tabs
- let s = self.styles[line_pos].remove(*pos);
- for _ in 0..4 {
- line.insert(*pos, ' ');
- self.styles[line_pos].insert(*pos, s);
- }
- }
- }
- }
+ pub fn render(&self) -> Vec<Vec<StyledString>> {
+ // Tabs are assumed to have been replaced by spaces in calling code.
+ assert!(self.text.iter().all(|r| !r.contains(&'\t')));
- pub fn render(&mut self) -> Vec<Vec<StyledString>> {
let mut output: Vec<Vec<StyledString>> = vec![];
let mut styled_vec: Vec<StyledString> = vec![];
- // before we render, replace tabs with spaces
- self.replace_tabs();
-
for (row, row_style) in self.text.iter().zip(&self.styles) {
let mut current_style = Style::NoStyle;
let mut current_text = String::new();
fn resolve_dollar_crates(&mut self);
fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment);
- fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension);
+ fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind);
fn expansion_for_ast_pass(
&mut self,
/// Advances the given peekable `Iterator` until it reaches a non-whitespace character.
fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) {
- while iter.peek().copied().map(|c| rustc_lexer::is_whitespace(c)) == Some(true) {
+ while iter.peek().copied().map(rustc_lexer::is_whitespace) == Some(true) {
iter.next();
}
}
fn to_opt_strs(self) -> Vec<Option<&'static str>> {
match self {
UnlabelledNodes(len) => vec![None; len],
- AllNodesLabelled(lbls) => lbls.into_iter().map(|l| Some(l)).collect(),
+ AllNodesLabelled(lbls) => lbls.into_iter().map(Some).collect(),
SomeNodesLabelled(lbls) => lbls.into_iter().collect(),
}
}
use std::io::{self, Read};
use std::path::Path;
-use rustc_serialize::opaque::Encoder;
+use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
/// The first few bytes of files generated by incremental compilation.
const FILE_MAGIC: &[u8] = b"RSIC";
/// the Git commit hash.
const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
-pub fn write_file_header(stream: &mut Encoder, nightly_build: bool) {
- stream.emit_raw_bytes(FILE_MAGIC);
- stream
- .emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]);
+pub fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) -> FileEncodeResult {
+ stream.emit_raw_bytes(FILE_MAGIC)?;
+ stream.emit_raw_bytes(&[
+ (HEADER_FORMAT_VERSION >> 0) as u8,
+ (HEADER_FORMAT_VERSION >> 8) as u8,
+ ])?;
let rustc_version = rustc_version(nightly_build);
assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
- stream.emit_raw_bytes(&[rustc_version.len() as u8]);
- stream.emit_raw_bytes(rustc_version.as_bytes());
+ stream.emit_raw_bytes(&[rustc_version.len() as u8])?;
+ stream.emit_raw_bytes(rustc_version.as_bytes())
}
/// Reads the contents of a file with a file header as defined in this module.
use rustc_data_structures::sync::join;
use rustc_middle::dep_graph::{DepGraph, DepKind, WorkProduct, WorkProductId};
use rustc_middle::ty::TyCtxt;
-use rustc_serialize::opaque::Encoder;
+use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use rustc_serialize::Encodable as RustcEncodable;
use rustc_session::Session;
use std::fs;
join(
move || {
sess.time("incr_comp_persist_result_cache", || {
- save_in(sess, query_cache_path, |e| encode_query_cache(tcx, e));
+ save_in(sess, query_cache_path, "query cache", |e| encode_query_cache(tcx, e));
});
},
|| {
sess.time("incr_comp_persist_dep_graph", || {
- save_in(sess, dep_graph_path, |e| {
+ save_in(sess, dep_graph_path, "dependency graph", |e| {
sess.time("incr_comp_encode_dep_graph", || encode_dep_graph(tcx, e))
});
});
debug!("save_work_product_index()");
dep_graph.assert_ignored();
let path = work_products_path(sess);
- save_in(sess, path, |e| encode_work_product_index(&new_work_products, e));
+ save_in(sess, path, "work product index", |e| encode_work_product_index(&new_work_products, e));
// We also need to clean out old work-products, as not all of them are
// deleted during invalidation. Some object files don't change their
});
}
-fn save_in<F>(sess: &Session, path_buf: PathBuf, encode: F)
+fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
where
- F: FnOnce(&mut Encoder),
+ F: FnOnce(&mut FileEncoder) -> FileEncodeResult,
{
debug!("save: storing data in {}", path_buf.display());
- // delete the old dep-graph, if any
+ // Delete the old file, if any.
// Note: It's important that we actually delete the old file and not just
// truncate and overwrite it, since it might be a shared hard-link, the
// underlying data of which we don't want to modify
Err(err) if err.kind() == io::ErrorKind::NotFound => (),
Err(err) => {
sess.err(&format!(
- "unable to delete old dep-graph at `{}`: {}",
+ "unable to delete old {} at `{}`: {}",
+ name,
path_buf.display(),
err
));
}
}
- // generate the data in a memory buffer
- let mut encoder = Encoder::new(Vec::new());
- file_format::write_file_header(&mut encoder, sess.is_nightly_build());
- encode(&mut encoder);
-
- // write the data out
- let data = encoder.into_inner();
- match fs::write(&path_buf, data) {
- Ok(_) => {
- debug!("save: data written to disk successfully");
- }
+ let mut encoder = match FileEncoder::new(&path_buf) {
+ Ok(encoder) => encoder,
Err(err) => {
- sess.err(&format!("failed to write dep-graph to `{}`: {}", path_buf.display(), err));
+ sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err));
+ return;
}
+ };
+
+ if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) {
+ sess.err(&format!("failed to write {} header to `{}`: {}", name, path_buf.display(), err));
+ return;
+ }
+
+ if let Err(err) = encode(&mut encoder) {
+ sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err));
+ return;
}
+
+ if let Err(err) = encoder.flush() {
+ sess.err(&format!("failed to flush {} to `{}`: {}", name, path_buf.display(), err));
+ return;
+ }
+
+ debug!("save: data written to disk successfully");
}
-fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut Encoder) {
+fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult {
// First encode the commandline arguments hash
- tcx.sess.opts.dep_tracking_hash().encode(encoder).unwrap();
+ tcx.sess.opts.dep_tracking_hash().encode(encoder)?;
// Encode the graph data.
let serialized_graph =
println!("[incremental]");
}
- tcx.sess.time("incr_comp_encode_serialized_dep_graph", || {
- serialized_graph.encode(encoder).unwrap();
- });
+ tcx.sess.time("incr_comp_encode_serialized_dep_graph", || serialized_graph.encode(encoder))
}
fn encode_work_product_index(
work_products: &FxHashMap<WorkProductId, WorkProduct>,
- encoder: &mut Encoder,
-) {
+ encoder: &mut FileEncoder,
+) -> FileEncodeResult {
let serialized_products: Vec<_> = work_products
.iter()
.map(|(id, work_product)| SerializedWorkProduct {
})
.collect();
- serialized_products.encode(encoder).unwrap();
+ serialized_products.encode(encoder)
}
-fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut Encoder) {
- tcx.sess.time("incr_comp_serialize_result_cache", || {
- tcx.serialize_query_result_cache(encoder).unwrap();
- })
+fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult {
+ tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder))
}
subst::{Subst, SubstsRef},
Region, Ty, TyCtxt, TypeFoldable,
};
-use rustc_span::{BytePos, DesugaringKind, Pos, Span};
+use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::ops::ControlFlow;
use std::{cmp, fmt};
// uh oh, hope no user ever sees THIS
ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None),
- ty::RePlaceholder(_) => ("any other region".to_string(), None),
+ ty::RePlaceholder(_) => return,
// FIXME(#13998) RePlaceholder should probably print like
// ReFree rather than dumping Debug output on the user.
self.check_and_note_conflicting_crates(diag, terr);
self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id());
+ if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values {
+ if let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() {
+ if let Some(def_id) = def_id.as_local() {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+ let span = self.tcx.hir().span(hir_id);
+ diag.span_note(span, "this closure does not fulfill the lifetime requirements");
+ }
+ }
+ }
+
// It reads better to have the error origin as the final
// thing.
self.note_error_origin(diag, cause, exp_found);
self.note_region_origin(&mut err, &sub_origin);
err.emit();
}
+
+ /// Determine whether an error associated with the given span and definition
+ /// should be treated as being caused by the implicit `From` conversion
+ /// within `?` desugaring.
+ pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool {
+ span.is_desugaring(DesugaringKind::QuestionMark)
+ && self.tcx.is_diagnostic_item(sym::from_trait, trait_def_id)
+ }
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
+use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat};
use rustc_middle::hir::map::Map;
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
use rustc_middle::ty::print::Print;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, DefIdTree, InferConst, Ty};
+use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::kw;
use rustc_span::Span;
found_closure: Option<&'tcx Expr<'tcx>>,
found_method_call: Option<&'tcx Expr<'tcx>>,
found_exact_method_call: Option<&'tcx Expr<'tcx>>,
+ found_use_diagnostic: Option<UseDiagnostic<'tcx>>,
}
impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
found_closure: None,
found_method_call: None,
found_exact_method_call: None,
+ found_use_diagnostic: None,
}
}
- fn node_ty_contains_target(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
- self.infcx
- .in_progress_typeck_results
- .and_then(|typeck_results| typeck_results.borrow().node_type_opt(hir_id))
- .map(|ty| self.infcx.resolve_vars_if_possible(ty))
- .filter(|ty| {
- ty.walk().any(|inner| {
- inner == self.target
- || match (inner.unpack(), self.target.unpack()) {
- (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
- use ty::{Infer, TyVar};
- match (inner_ty.kind(), target_ty.kind()) {
- (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self
- .infcx
- .inner
- .borrow_mut()
- .type_variables()
- .sub_unified(a_vid, b_vid),
- _ => false,
- }
+ fn node_type_opt(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
+ self.infcx.in_progress_typeck_results?.borrow().node_type_opt(hir_id)
+ }
+
+ fn node_ty_contains_target(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
+ self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| {
+ ty.walk().any(|inner| {
+ inner == self.target
+ || match (inner.unpack(), self.target.unpack()) {
+ (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
+ use ty::{Infer, TyVar};
+ match (inner_ty.kind(), target_ty.kind()) {
+ (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self
+ .infcx
+ .inner
+ .borrow_mut()
+ .type_variables()
+ .sub_unified(a_vid, b_vid),
+ _ => false,
}
- _ => false,
}
- })
+ _ => false,
+ }
})
+ })
+ }
+
+ /// Determine whether the expression, assumed to be the callee within a `Call`,
+ /// corresponds to the `From::from` emitted in desugaring of the `?` operator.
+ fn is_try_conversion(&self, callee: &Expr<'tcx>) -> bool {
+ self.infcx
+ .trait_def_from_hir_fn(callee.hir_id)
+ .map_or(false, |def_id| self.infcx.is_try_conversion(callee.span, def_id))
}
}
// are handled specially, but instead they should be handled in `annotate_method_call`,
// which currently doesn't work because this evaluates to `false` for const arguments.
// See https://github.com/rust-lang/rust/pull/77758 for more details.
- if self.node_ty_contains_target(expr.hir_id).is_some() {
+ if let Some(ty) = self.node_ty_contains_target(expr.hir_id) {
match expr.kind {
ExprKind::Closure(..) => self.found_closure = Some(&expr),
ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
+
+ // If the given expression falls within the target span and is a
+ // `From::from(e)` call emitted during desugaring of the `?` operator,
+ // extract the types inferred before and after the call
+ ExprKind::Call(callee, [arg])
+ if self.target_span.contains(expr.span)
+ && self.found_use_diagnostic.is_none()
+ && self.is_try_conversion(callee) =>
+ {
+ self.found_use_diagnostic = self.node_type_opt(arg.hir_id).map(|pre_ty| {
+ UseDiagnostic::TryConversion { pre_ty, post_ty: ty, span: callee.span }
+ });
+ }
_ => {}
}
}
}
}
+/// An observation about the use site of a type to be emitted as an additional
+/// note in an inference failure error.
+enum UseDiagnostic<'tcx> {
+ /// Records the types inferred before and after `From::from` is called on the
+ /// error value within the desugaring of the `?` operator.
+ TryConversion { pre_ty: Ty<'tcx>, post_ty: Ty<'tcx>, span: Span },
+}
+
+impl UseDiagnostic<'_> {
+ /// Return a descriptor of the value at the use site
+ fn descr(&self) -> &'static str {
+ match self {
+ Self::TryConversion { .. } => "error for `?` operator",
+ }
+ }
+
+ /// Return a descriptor of the type at the use site
+ fn type_descr(&self) -> &'static str {
+ match self {
+ Self::TryConversion { .. } => "error type for `?` operator",
+ }
+ }
+
+ fn applies_to(&self, span: Span) -> bool {
+ match *self {
+ // In some cases the span for an inference failure due to try
+ // conversion contains the antecedent expression as well as the `?`
+ Self::TryConversion { span: s, .. } => span.contains(s) && span.hi() == s.hi(),
+ }
+ }
+
+ fn attach_note(&self, err: &mut DiagnosticBuilder<'_>) {
+ match *self {
+ Self::TryConversion { pre_ty, post_ty, .. } => {
+ let intro = "`?` implicitly converts the error value";
+
+ let msg = match (pre_ty.is_ty_infer(), post_ty.is_ty_infer()) {
+ (true, true) => format!("{} using the `From` trait", intro),
+ (false, true) => {
+ format!("{} into a type implementing `From<{}>`", intro, pre_ty)
+ }
+ (true, false) => {
+ format!("{} into `{}` using the `From` trait", intro, post_ty)
+ }
+ (false, false) => {
+ format!(
+ "{} into `{}` using its implementation of `From<{}>`",
+ intro, post_ty, pre_ty
+ )
+ }
+ };
+
+ err.note(&msg);
+ }
+ }
+ }
+}
+
/// Suggest giving an appropriate return type to a closure expression.
fn closure_return_type_suggestion(
- span: Span,
err: &mut DiagnosticBuilder<'_>,
output: &FnRetTy<'_>,
body: &Body<'_>,
- descr: &str,
- name: &str,
ret: &str,
- parent_name: Option<String>,
- parent_descr: Option<&str>,
) {
let (arrow, post) = match output {
FnRetTy::DefaultReturn(_) => ("-> ", " "),
suggestion,
Applicability::HasPlaceholders,
);
- err.span_label(
- span,
- InferCtxt::cannot_infer_msg("type", &name, &descr, parent_name, parent_descr),
- );
}
/// Given a closure signature, return a `String` containing a list of all its argument types.
pub struct InferenceDiagnosticsData {
pub name: String,
pub span: Option<Span>,
- pub description: Cow<'static, str>,
- pub parent_name: Option<String>,
- pub parent_description: Option<&'static str>,
+ pub kind: UnderspecifiedArgKind,
+ pub parent: Option<InferenceDiagnosticsParentData>,
+}
+
+/// Data on the parent definition where a generic argument was declared.
+pub struct InferenceDiagnosticsParentData {
+ pub prefix: &'static str,
+ pub name: String,
+}
+
+pub enum UnderspecifiedArgKind {
+ Type { prefix: Cow<'static, str> },
+ Const { is_parameter: bool },
+}
+
+impl InferenceDiagnosticsData {
+ /// Generate a label for a generic argument which can't be inferred. When not
+ /// much is known about the argument, `use_diag` may be used to describe the
+ /// labeled value.
+ fn cannot_infer_msg(&self, use_diag: Option<&UseDiagnostic<'_>>) -> String {
+ if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
+ if let Some(use_diag) = use_diag {
+ return format!("cannot infer type of {}", use_diag.descr());
+ }
+
+ return "cannot infer type".to_string();
+ }
+
+ let suffix = match (&self.parent, use_diag) {
+ (Some(parent), _) => format!(" declared on the {} `{}`", parent.prefix, parent.name),
+ (None, Some(use_diag)) => format!(" in {}", use_diag.type_descr()),
+ (None, None) => String::new(),
+ };
+
+ // For example: "cannot infer type for type parameter `T`"
+ format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
+ }
+}
+
+impl InferenceDiagnosticsParentData {
+ fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
+ let parent_def_id = tcx.parent(def_id)?;
+
+ let parent_name =
+ tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
+
+ Some(InferenceDiagnosticsParentData {
+ prefix: tcx.def_kind(parent_def_id).descr(parent_def_id),
+ name: parent_name,
+ })
+ }
+}
+
+impl UnderspecifiedArgKind {
+ fn prefix_string(&self) -> Cow<'static, str> {
+ match self {
+ Self::Type { prefix } => format!("type for {}", prefix).into(),
+ Self::Const { is_parameter: true } => "the value of const parameter".into(),
+ Self::Const { is_parameter: false } => "the value of the constant".into(),
+ }
+ }
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) =
var_origin.kind
{
- let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id));
- let (parent_name, parent_description) =
- if let Some(parent_def_id) = parent_def_id {
- let parent_name = self
- .tcx
- .def_key(parent_def_id)
- .disambiguated_data
- .data
- .get_opt_name()
- .map(|parent_symbol| parent_symbol.to_string());
-
- (
- parent_name,
- Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
- )
- } else {
- (None, None)
- };
-
if name != kw::SelfUpper {
return InferenceDiagnosticsData {
name: name.to_string(),
span: Some(var_origin.span),
- description: "type parameter".into(),
- parent_name,
- parent_description,
+ kind: UnderspecifiedArgKind::Type {
+ prefix: "type parameter".into(),
+ },
+ parent: def_id.and_then(|def_id| {
+ InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id)
+ }),
};
}
}
InferenceDiagnosticsData {
name: s,
span: None,
- description: ty.prefix_string(),
- parent_name: None,
- parent_description: None,
+ kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string() },
+ parent: None,
}
}
GenericArgKind::Const(ct) => {
if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) =
origin.kind
{
- let parent_def_id = self.tcx.parent(def_id);
- let (parent_name, parent_description) =
- if let Some(parent_def_id) = parent_def_id {
- let parent_name = self
- .tcx
- .def_key(parent_def_id)
- .disambiguated_data
- .data
- .get_opt_name()
- .map(|parent_symbol| parent_symbol.to_string());
-
- (
- parent_name,
- Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
- )
- } else {
- (None, None)
- };
-
return InferenceDiagnosticsData {
name: name.to_string(),
span: Some(origin.span),
- description: "const parameter".into(),
- parent_name,
- parent_description,
+ kind: UnderspecifiedArgKind::Const { is_parameter: true },
+ parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
};
}
InferenceDiagnosticsData {
name: s,
span: Some(origin.span),
- description: "the constant".into(),
- parent_name: None,
- parent_description: None,
+ kind: UnderspecifiedArgKind::Const { is_parameter: false },
+ parent: None,
}
} else {
bug!("unexpect const: {:?}", ct);
// When `arg_data.name` corresponds to a type argument, show the path of the full type we're
// trying to infer. In the following example, `ty_msg` contains
- // " in `std::result::Result<i32, E>`":
+ // " for `std::result::Result<i32, E>`":
// ```
// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
// --> file.rs:L:CC
error_code,
);
+ let use_diag = local_visitor.found_use_diagnostic.as_ref();
+ if let Some(use_diag) = use_diag {
+ if use_diag.applies_to(err_span) {
+ use_diag.attach_note(&mut err);
+ }
+ }
+
let suffix = match local_visitor.found_node_ty {
Some(ty) if ty.is_closure() => {
let substs =
if let Some((decl, body_id)) = closure_decl_and_body_id {
closure_return_type_suggestion(
- span,
&mut err,
&decl.output,
self.tcx.hir().body(body_id),
- &arg_data.description,
- &arg_data.name,
&ret,
- arg_data.parent_name,
- arg_data.parent_description,
);
// We don't want to give the other suggestions when the problem is the
// closure return type.
+ err.span_label(
+ span,
+ arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
+ );
return err;
}
// |
// = note: type must be known at this point
let span = arg_data.span.unwrap_or(err_span);
+
+ // Avoid multiple labels pointing at `span`.
if !err
.span
.span_labels()
.any(|span_label| span_label.label.is_some() && span_label.span == span)
&& local_visitor.found_arg_pattern.is_none()
{
- let (kind_str, const_value) = match arg.unpack() {
- GenericArgKind::Type(_) => ("type", None),
- GenericArgKind::Const(_) => ("the value", Some(())),
- GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
- };
-
// FIXME(const_generics): we would like to handle const arguments
// as part of the normal diagnostics flow below, but there appear to
// be subtleties in doing so, so for now we special-case const args
// here.
- if let Some(suggestion) = const_value
- .and_then(|_| arg_data.parent_name.as_ref())
- .map(|parent| format!("{}::<{}>", parent, arg_data.name))
+ if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) =
+ (&arg_data.kind, &arg_data.parent)
{
err.span_suggestion_verbose(
span,
"consider specifying the const argument",
- suggestion,
+ format!("{}::<{}>", parent_data.name, arg_data.name),
Applicability::MaybeIncorrect,
);
}
- // Avoid multiple labels pointing at `span`.
err.span_label(
span,
- InferCtxt::cannot_infer_msg(
- kind_str,
- &arg_data.name,
- &arg_data.description,
- arg_data.parent_name,
- arg_data.parent_description,
- ),
+ arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
);
}
err
}
+ fn trait_def_from_hir_fn(&self, hir_id: hir::HirId) -> Option<DefId> {
+ // The DefId will be the method's trait item ID unless this is an inherent impl
+ if let Some((DefKind::AssocFn, def_id)) =
+ self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id)
+ {
+ return self
+ .tcx
+ .parent(def_id)
+ .filter(|&parent_def_id| self.tcx.is_trait(parent_def_id));
+ }
+
+ None
+ }
+
/// If the `FnSig` for the method call can be found and type arguments are identified as
/// needed, suggest annotating the call, otherwise point out the resulting type of the call.
fn annotate_method_call(
"type inside {} must be known in this context",
kind,
);
- err.span_label(
- span,
- InferCtxt::cannot_infer_msg(
- "type",
- &data.name,
- &data.description,
- data.parent_name,
- data.parent_description,
- ),
- );
+ err.span_label(span, data.cannot_infer_msg(None));
err
}
-
- fn cannot_infer_msg(
- kind_str: &str,
- type_name: &str,
- descr: &str,
- parent_name: Option<String>,
- parent_descr: Option<&str>,
- ) -> String {
- if type_name == "_" {
- format!("cannot infer {}", kind_str)
- } else {
- let parent_desc = if let Some(parent_name) = parent_name {
- let parent_type_descr = if let Some(parent_descr) = parent_descr {
- format!(" the {}", parent_descr)
- } else {
- "".into()
- };
-
- format!(" declared on{} `{}`", parent_type_descr, parent_name)
- } else {
- "".to_string()
- };
-
- // FIXME: We really shouldn't be dealing with strings here
- // but instead use a sensible enum for cases like this.
- let preposition = if "the value" == kind_str { "of" } else { "for" };
- // For example: "cannot infer type for type parameter `T`"
- format!(
- "cannot infer {} {} {} `{}`{}",
- kind_str, preposition, descr, type_name, parent_desc
- )
- }
- }
}
use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
use crate::infer::{self, InferCtxt, SubregionOrigin};
use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{self, Region};
infer::Subtype(box trace) => {
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
let mut err = self.report_and_explain_type_error(trace, &terr);
- note_and_explain_region(self.tcx, &mut err, "", sup, "...");
- note_and_explain_region(
- self.tcx,
- &mut err,
- "...does not necessarily outlive ",
- sub,
- "",
- );
+ match (sub, sup) {
+ (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+ (ty::RePlaceholder(_), _) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "",
+ sup,
+ " doesn't meet the lifetime requirements",
+ );
+ }
+ (_, ty::RePlaceholder(_)) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the required lifetime does not necessarily outlive ",
+ sub,
+ "",
+ );
+ }
+ _ => {
+ note_and_explain_region(self.tcx, &mut err, "", sup, "...");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...does not necessarily outlive ",
+ sub,
+ "",
+ );
+ }
+ }
err
}
infer::Reborrow(span) => {
sup: Region<'tcx>,
) -> DiagnosticBuilder<'tcx> {
// I can't think how to do better than this right now. -nikomatsakis
+ debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
match placeholder_origin {
+ infer::Subtype(box ref trace)
+ if matches!(
+ &trace.cause.code.peel_derives(),
+ ObligationCauseCode::BindingObligation(..)
+ ) =>
+ {
+ // Hack to get around the borrow checker because trace.cause has an `Rc`.
+ if let ObligationCauseCode::BindingObligation(_, span) =
+ &trace.cause.code.peel_derives()
+ {
+ let span = *span;
+ let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
+ err.span_note(span, "the lifetime requirement is introduced here");
+ err
+ } else {
+ unreachable!()
+ }
+ }
infer::Subtype(box trace) => {
let terr = TypeError::RegionsPlaceholderMismatch;
- self.report_and_explain_type_error(trace, &terr)
+ return self.report_and_explain_type_error(trace, &terr);
}
-
- _ => self.report_concrete_failure(placeholder_origin, sub, sup),
+ _ => return self.report_concrete_failure(placeholder_origin, sub, sup),
}
}
}
pre_expansion_lint(sess, lint_store, &krate);
let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas);
- rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition());
+ rustc_builtin_macros::register_builtin_macros(&mut resolver);
krate = sess.time("crate_injection", || {
let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes};
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
use rustc_session::config::{
- Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion,
+ Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, WasiExecModel,
};
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
tracked!(unleash_the_miri_inside_of_you, true);
tracked!(use_ctors_section, Some(true));
tracked!(verify_llvm_ir, true);
+ tracked!(wasi_exec_model, Some(WasiExecModel::Reactor));
}
#[test]
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, Subst};
+use rustc_middle::ty::Instance;
use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
use rustc_session::Session;
use rustc_span::edition::Edition;
}
pub struct ClashingExternDeclarations {
- seen_decls: FxHashMap<Symbol, HirId>,
+ /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls
+ /// contains an entry for key K, it means a symbol with name K has been seen by this lint and
+ /// the symbol should be reported as a clashing declaration.
+ // FIXME: Technically, we could just store a &'tcx str here without issue; however, the
+ // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
+ seen_decls: FxHashMap<String, HirId>,
}
/// Differentiate between whether the name for an extern decl came from the link_name attribute or
fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<HirId> {
let hid = fi.hir_id;
- let name =
- &tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name);
-
- if self.seen_decls.contains_key(name) {
+ let local_did = tcx.hir().local_def_id(fi.hir_id);
+ let did = local_did.to_def_id();
+ let instance = Instance::new(did, ty::List::identity_for_item(tcx, did));
+ let name = tcx.symbol_name(instance).name;
+ if let Some(&hir_id) = self.seen_decls.get(name) {
// Avoid updating the map with the new entry when we do find a collision. We want to
// make sure we're always pointing to the first definition as the previous declaration.
// This lets us avoid emitting "knock-on" diagnostics.
- Some(*self.seen_decls.get(name).unwrap())
+ Some(hir_id)
} else {
- self.seen_decls.insert(*name, hid)
+ self.seen_decls.insert(name.to_owned(), hid)
}
}
}
}
+ fn is_ctfe_mir_available(&self, id: DefIndex) -> bool {
+ self.root.tables.mir_for_ctfe.get(self, id).is_some()
+ }
+
fn is_item_mir_available(&self, id: DefIndex) -> bool {
self.root.tables.mir.get(self, id).is_some()
}
.decode((self, tcx))
}
+ fn get_mir_for_ctfe(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> {
+ self.root
+ .tables
+ .mir_for_ctfe
+ .get(self, id)
+ .unwrap_or_else(|| {
+ bug!("get_mir_for_ctfe: missing MIR for `{:?}`", self.local_def_id(id))
+ })
+ .decode((self, tcx))
+ }
+
fn get_mir_abstract_const(
&self,
tcx: TyCtxt<'tcx>,
})
}
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
+ mir_for_ctfe => { tcx.arena.alloc(cdata.get_mir_for_ctfe(tcx, def_id.index)) }
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) }
unused_generic_params => { cdata.get_unused_generic_params(def_id.index) }
impl_parent => { cdata.get_parent_impl(def_id.index) }
trait_of_item => { cdata.get_trait_of_item(def_id.index) }
is_mir_available => { cdata.is_item_mir_available(def_id.index) }
+ is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
is_panic_runtime => { cdata.root.panic_runtime }
impl<'a, 'tcx> FingerprintEncoder for EncodeContext<'a, 'tcx> {
fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), Self::Error> {
- f.encode_opaque(&mut self.opaque)
+ self.opaque.encode_fingerprint(f)
}
}
self.encode_generics(def_id);
self.encode_explicit_predicates(def_id);
self.encode_inferred_outlives(def_id);
- self.encode_optimized_mir(def_id.expect_local());
- self.encode_promoted_mir(def_id.expect_local());
}
fn encode_enum_variant_ctor(&mut self, def: &ty::AdtDef, index: VariantIdx) {
self.encode_generics(def_id);
self.encode_explicit_predicates(def_id);
self.encode_inferred_outlives(def_id);
+ self.encode_mir_for_ctfe(def_id.expect_local());
self.encode_optimized_mir(def_id.expect_local());
self.encode_promoted_mir(def_id.expect_local());
}
self.encode_explicit_predicates(def_id);
self.encode_inferred_outlives(def_id);
self.encode_optimized_mir(def_id.expect_local());
+ self.encode_mir_for_ctfe(def_id.expect_local());
self.encode_promoted_mir(def_id.expect_local());
}
self.encode_inferred_outlives(def_id);
// This should be kept in sync with `PrefetchVisitor.visit_trait_item`.
- self.encode_optimized_mir(def_id.expect_local());
- self.encode_promoted_mir(def_id.expect_local());
+ match trait_item.kind {
+ ty::AssocKind::Type => {}
+ ty::AssocKind::Const => {
+ if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id.expect_local()) {
+ self.encode_mir_for_ctfe(def_id.expect_local());
+ self.encode_promoted_mir(def_id.expect_local());
+ }
+ }
+ ty::AssocKind::Fn => {
+ if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id.expect_local()) {
+ self.encode_optimized_mir(def_id.expect_local());
+ self.encode_promoted_mir(def_id.expect_local());
+ }
+ }
+ }
}
fn metadata_output_only(&self) -> bool {
// The following part should be kept in sync with `PrefetchVisitor.visit_impl_item`.
- let mir = match ast_item.kind {
- hir::ImplItemKind::Const(..) => true,
+ let (mir, mir_const) = match ast_item.kind {
+ hir::ImplItemKind::Const(..) => (false, true),
hir::ImplItemKind::Fn(ref sig, _) => {
let generics = self.tcx.generics_of(def_id);
let needs_inline = (generics.requires_monomorphization(self.tcx)
&& !self.metadata_output_only();
let is_const_fn = sig.header.constness == hir::Constness::Const;
let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir;
- needs_inline || is_const_fn || always_encode_mir
+ (needs_inline || always_encode_mir, is_const_fn)
}
- hir::ImplItemKind::TyAlias(..) => false,
+ hir::ImplItemKind::TyAlias(..) => (false, false),
};
if mir {
self.encode_optimized_mir(def_id.expect_local());
+ }
+ if mir || mir_const {
self.encode_promoted_mir(def_id.expect_local());
}
+ if mir_const {
+ self.encode_mir_for_ctfe(def_id.expect_local());
+ }
}
fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Ident]> {
self.lazy(param_names.iter())
}
- fn encode_optimized_mir(&mut self, def_id: LocalDefId) {
- debug!("EntryBuilder::encode_mir({:?})", def_id);
- if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) {
- record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
+ fn encode_mir_for_ctfe(&mut self, def_id: LocalDefId) {
+ debug!("EntryBuilder::encode_mir_for_ctfe({:?})", def_id);
+ record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
- let unused = self.tcx.unused_generic_params(def_id);
- if !unused.is_empty() {
- record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
- }
+ let unused = self.tcx.unused_generic_params(def_id);
+ if !unused.is_empty() {
+ record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
+ }
- let abstract_const = self.tcx.mir_abstract_const(def_id);
- if let Ok(Some(abstract_const)) = abstract_const {
- record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const);
- }
+ let abstract_const = self.tcx.mir_abstract_const(def_id);
+ if let Ok(Some(abstract_const)) = abstract_const {
+ record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const);
+ }
+ }
+
+ fn encode_optimized_mir(&mut self, def_id: LocalDefId) {
+ debug!("EntryBuilder::encode_optimized_mir({:?})", def_id);
+ record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
+
+ let unused = self.tcx.unused_generic_params(def_id);
+ if !unused.is_empty() {
+ record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
}
}
fn encode_promoted_mir(&mut self, def_id: LocalDefId) {
debug!("EncodeContext::encode_promoted_mir({:?})", def_id);
- if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) {
- record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
- }
+ record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
}
// Encodes the inherent implementations of a structure, enumeration, or trait.
// The following part should be kept in sync with `PrefetchVisitor.visit_item`.
- let mir = match item.kind {
- hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => true,
+ let (mir, const_mir) = match item.kind {
+ hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => (false, true),
hir::ItemKind::Fn(ref sig, ..) => {
let generics = tcx.generics_of(def_id);
let needs_inline = (generics.requires_monomorphization(tcx)
|| tcx.codegen_fn_attrs(def_id).requests_inline())
&& !self.metadata_output_only();
+
+ let is_const_fn = sig.header.constness == hir::Constness::Const;
let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir;
- needs_inline || sig.header.constness == hir::Constness::Const || always_encode_mir
+ let mir = needs_inline || always_encode_mir;
+ // We don't need the optimized MIR for const fns.
+ (mir, is_const_fn)
}
- _ => false,
+ _ => (false, false),
};
if mir {
self.encode_optimized_mir(def_id.expect_local());
+ }
+ if mir || const_mir {
self.encode_promoted_mir(def_id.expect_local());
}
+ if const_mir {
+ self.encode_mir_for_ctfe(def_id.expect_local());
+ }
}
/// Serialize the text of exported macros
self.encode_generics(def_id.to_def_id());
self.encode_explicit_predicates(def_id.to_def_id());
self.encode_inferred_outlives(def_id.to_def_id());
- self.encode_optimized_mir(def_id);
+ self.encode_mir_for_ctfe(def_id);
self.encode_promoted_mir(def_id);
}
}
impl<'tcx> PrefetchVisitor<'tcx> {
+ fn prefetch_ctfe_mir(&self, def_id: LocalDefId) {
+ if self.mir_keys.contains(&def_id) {
+ self.tcx.ensure().mir_for_ctfe(def_id);
+ self.tcx.ensure().promoted_mir(def_id);
+ }
+ }
fn prefetch_mir(&self, def_id: LocalDefId) {
if self.mir_keys.contains(&def_id) {
self.tcx.ensure().optimized_mir(def_id);
let tcx = self.tcx;
match item.kind {
hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
- self.prefetch_mir(tcx.hir().local_def_id(item.hir_id))
+ self.prefetch_ctfe_mir(tcx.hir().local_def_id(item.hir_id))
}
hir::ItemKind::Fn(ref sig, ..) => {
let def_id = tcx.hir().local_def_id(item.hir_id);
let generics = tcx.generics_of(def_id.to_def_id());
let needs_inline = generics.requires_monomorphization(tcx)
|| tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline();
- if needs_inline || sig.header.constness == hir::Constness::Const {
+ if needs_inline {
self.prefetch_mir(def_id)
}
+ if sig.header.constness == hir::Constness::Const {
+ self.prefetch_ctfe_mir(def_id);
+ }
}
_ => (),
}
fn visit_trait_item(&self, trait_item: &'v hir::TraitItem<'v>) {
// This should be kept in sync with `encode_info_for_trait_item`.
- self.prefetch_mir(self.tcx.hir().local_def_id(trait_item.hir_id));
+ let def_id = self.tcx.hir().local_def_id(trait_item.hir_id);
+ match trait_item.kind {
+ hir::TraitItemKind::Type(..) => {}
+ hir::TraitItemKind::Const(..) => {
+ self.prefetch_ctfe_mir(def_id);
+ }
+ hir::TraitItemKind::Fn(..) => {
+ self.prefetch_mir(def_id);
+ }
+ }
}
fn visit_impl_item(&self, impl_item: &'v hir::ImplItem<'v>) {
let tcx = self.tcx;
match impl_item.kind {
hir::ImplItemKind::Const(..) => {
- self.prefetch_mir(tcx.hir().local_def_id(impl_item.hir_id))
+ self.prefetch_ctfe_mir(tcx.hir().local_def_id(impl_item.hir_id))
}
hir::ImplItemKind::Fn(ref sig, _) => {
let def_id = tcx.hir().local_def_id(impl_item.hir_id);
let needs_inline = generics.requires_monomorphization(tcx)
|| tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline();
let is_const_fn = sig.header.constness == hir::Constness::Const;
- if needs_inline || is_const_fn {
+ if needs_inline {
self.prefetch_mir(def_id)
}
+ if is_const_fn {
+ self.prefetch_ctfe_mir(def_id);
+ }
}
hir::ImplItemKind::TyAlias(..) => (),
}
// As an optimization, a missing entry indicates an empty `&[]`.
explicit_item_bounds: Table<DefIndex, Lazy!([(ty::Predicate<'tcx>, Span)])>,
mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
+ mir_for_ctfe: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
mir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [mir::abstract_const::Node<'tcx>])>,
unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u32>>>,
let mut scope = id;
loop {
scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID);
- if scope == CRATE_HIR_ID {
- return CRATE_HIR_ID;
- }
- match self.get(scope) {
- Node::Block(_) => {}
- _ => break,
+ if scope == CRATE_HIR_ID || !matches!(self.get(scope), Node::Block(_)) {
+ return scope;
}
}
- scope
}
pub fn get_parent_did(&self, id: HirId) -> LocalDefId {
use crate::dep_graph::{dep_constructor, DepNode, WorkProduct, WorkProductId};
use crate::ich::{NodeIdHashingMode, StableHashingContext};
use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt};
+use rustc_attr::InlineAttr;
use rustc_data_structures::base_n;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
}
pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode {
+ let generate_cgu_internal_copies = tcx
+ .sess
+ .opts
+ .debugging_opts
+ .inline_in_all_cgus
+ .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No)
+ && !tcx.sess.link_dead_code();
+
match *self {
MonoItem::Fn(ref instance) => {
let entry_def_id = tcx.entry_fn(LOCAL_CRATE).map(|(id, _)| id);
return InstantiationMode::GloballyShared { may_conflict: false };
}
- let generate_cgu_internal_copies = tcx
- .sess
- .opts
- .debugging_opts
- .inline_in_all_cgus
- .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No)
- && !tcx.sess.link_dead_code();
-
// At this point we don't have explicit linkage and we're an
- // inlined function. If we should generate local copies for each CGU,
- // then return `LocalCopy`, otherwise we'll just generate one copy
- // and share it with all CGUs in this crate.
+ // inlined function. If we're inlining into all CGUs then we'll
+ // be creating a local copy per CGU.
if generate_cgu_internal_copies {
- InstantiationMode::LocalCopy
- } else {
- // Finally, if we've reached this point, then we should optimize for
- // compilation speed. In that regard, we will ignore any `#[inline]`
- // annotations on the function and simply codegen it as usual. This could
- // conflict with upstream crates as it could be an exported symbol.
- InstantiationMode::GloballyShared { may_conflict: true }
+ return InstantiationMode::LocalCopy;
+ }
+
+ // Finally, if this is `#[inline(always)]` we're sure to respect
+ // that with an inline copy per CGU, but otherwise we'll be
+ // creating one copy of this `#[inline]` function which may
+ // conflict with upstream crates as it could be an exported
+ // symbol.
+ match tcx.codegen_fn_attrs(instance.def_id()).inline {
+ InlineAttr::Always => InstantiationMode::LocalCopy,
+ _ => InstantiationMode::GloballyShared { may_conflict: true },
}
}
MonoItem::Static(..) | MonoItem::GlobalAsm(..) => {
}
#[inline]
- pub fn optimized_mir_opt_const_arg(
+ pub fn optimized_mir_or_const_arg_mir(
self,
def: ty::WithOptConstParam<DefId>,
) -> &'tcx Body<'tcx> {
if let Some((did, param_did)) = def.as_const_arg() {
- self.optimized_mir_of_const_arg((did, param_did))
+ self.mir_for_ctfe_of_const_arg((did, param_did))
} else {
self.optimized_mir(def.did)
}
}
+ #[inline]
+ pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam<DefId>) -> &'tcx Body<'tcx> {
+ if let Some((did, param_did)) = def.as_const_arg() {
+ self.mir_for_ctfe_of_const_arg((did, param_did))
+ } else {
+ self.mir_for_ctfe(def.did)
+ }
+ }
+
#[inline]
pub fn mir_abstract_const_opt_const_arg(
self,
desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) }
}
+ query mir_for_ctfe(
+ key: DefId
+ ) -> &'tcx mir::Body<'tcx> {
+ desc { |tcx| "caching mir of `{}` for CTFE", tcx.def_path_str(key) }
+ cache_on_disk_if { key.is_local() }
+ }
+
+ query mir_for_ctfe_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> {
+ desc {
+ |tcx| "MIR for CTFE of the const argument `{}`",
+ tcx.def_path_str(key.0.to_def_id())
+ }
+ }
+
query mir_promoted(key: ty::WithOptConstParam<LocalDefId>) ->
(
&'tcx Steal<mir::Body<'tcx>>,
desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
}
- query optimized_mir_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> {
- desc {
- |tcx| "optimizing MIR for the const argument `{}`",
- tcx.def_path_str(key.0.to_def_id())
- }
- }
/// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
/// MIR pass (assuming the -Zinstrument-coverage option is enabled).
}
Codegen {
+ query is_ctfe_mir_available(key: DefId) -> bool {
+ desc { |tcx| "checking if item has ctfe mir available: `{}`", tcx.def_path_str(key) }
+ }
query is_mir_available(key: DefId) -> bool {
desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) }
}
eval_always
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
}
+
+ /// Computes the set of modules from which this type is visibly uninhabited.
+ /// To check whether a type is uninhabited at all (not just from a given module), you could
+ /// check whether the forest is empty.
+ query type_uninhabited_from(
+ key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
+ ) -> ty::inhabitedness::DefIdForest {
+ desc { "computing the inhabitedness of `{:?}`", key }
+ }
}
Other {
}
}
-pub trait OpaqueEncoder: Encoder {
- fn opaque(&mut self) -> &mut rustc_serialize::opaque::Encoder;
- fn encoder_position(&self) -> usize;
-}
-
-impl OpaqueEncoder for rustc_serialize::opaque::Encoder {
- #[inline]
- fn opaque(&mut self) -> &mut rustc_serialize::opaque::Encoder {
- self
- }
- #[inline]
- fn encoder_position(&self) -> usize {
- self.position()
- }
-}
-
pub trait TyEncoder<'tcx>: Encoder {
const CLEAR_CROSS_CRATE: bool;
let item_id = tcx.hir().get_parent_node(hir_id);
let item_def_id = tcx.hir().local_def_id(item_id);
let generics = tcx.generics_of(item_def_id.to_def_id());
- let index =
- generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()];
+ let index = generics.param_def_id_to_index[&def_id];
let name = tcx.hir().name(hir_id);
ty::ConstKind::Param(ty::ParamConst::new(index, name))
}
};
use rustc_index::vec::{Idx, IndexVec};
use rustc_macros::HashStable;
+use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames};
use rustc_session::lint::{Level, Lint};
use rustc_session::Session;
}
}
- pub fn serialize_query_result_cache<E>(self, encoder: &mut E) -> Result<(), E::Error>
- where
- E: ty::codec::OpaqueEncoder,
- {
+ pub fn serialize_query_result_cache(self, encoder: &mut FileEncoder) -> FileEncodeResult {
self.queries.on_disk_cache.as_ref().map(|c| c.serialize(self, encoder)).unwrap_or(Ok(()))
}
use rustc_hir::CRATE_HIR_ID;
use smallvec::SmallVec;
use std::mem;
+use std::sync::Arc;
+
+use DefIdForest::*;
/// Represents a forest of `DefId`s closed under the ancestor relation. That is,
/// if a `DefId` representing a module is contained in the forest then all
///
/// This is used to represent a set of modules in which a type is visibly
/// uninhabited.
-#[derive(Clone)]
-pub struct DefIdForest {
- /// The minimal set of `DefId`s required to represent the whole set.
- /// If A and B are DefIds in the `DefIdForest`, and A is a descendant
- /// of B, then only B will be in `root_ids`.
- /// We use a `SmallVec` here because (for its use for caching inhabitedness)
- /// it's rare that this will contain even two IDs.
- root_ids: SmallVec<[DefId; 1]>,
+///
+/// We store the minimal set of `DefId`s required to represent the whole set. If A and B are
+/// `DefId`s in the `DefIdForest`, and A is a parent of B, then only A will be stored. When this is
+/// used with `type_uninhabited_from`, there will very rarely be more than one `DefId` stored.
+#[derive(Clone, HashStable)]
+pub enum DefIdForest {
+ Empty,
+ Single(DefId),
+ /// This variant is very rare.
+ /// Invariant: >1 elements
+ /// We use `Arc` because this is used in the output of a query.
+ Multiple(Arc<[DefId]>),
+}
+
+/// Tests whether a slice of roots contains a given DefId.
+#[inline]
+fn slice_contains(tcx: TyCtxt<'tcx>, slice: &[DefId], id: DefId) -> bool {
+ slice.iter().any(|root_id| tcx.is_descendant_of(id, *root_id))
}
impl<'tcx> DefIdForest {
/// Creates an empty forest.
pub fn empty() -> DefIdForest {
- DefIdForest { root_ids: SmallVec::new() }
+ DefIdForest::Empty
}
/// Creates a forest consisting of a single tree representing the entire
/// crate.
#[inline]
pub fn full(tcx: TyCtxt<'tcx>) -> DefIdForest {
- let crate_id = tcx.hir().local_def_id(CRATE_HIR_ID);
- DefIdForest::from_id(crate_id.to_def_id())
+ DefIdForest::from_id(tcx.hir().local_def_id(CRATE_HIR_ID).to_def_id())
}
/// Creates a forest containing a `DefId` and all its descendants.
pub fn from_id(id: DefId) -> DefIdForest {
- let mut root_ids = SmallVec::new();
- root_ids.push(id);
- DefIdForest { root_ids }
+ DefIdForest::Single(id)
+ }
+
+ fn as_slice(&self) -> &[DefId] {
+ match self {
+ Empty => &[],
+ Single(id) => std::slice::from_ref(id),
+ Multiple(root_ids) => root_ids,
+ }
+ }
+
+ // Only allocates in the rare `Multiple` case.
+ fn from_slice(root_ids: &[DefId]) -> DefIdForest {
+ match root_ids {
+ [] => Empty,
+ [id] => Single(*id),
+ _ => DefIdForest::Multiple(root_ids.into()),
+ }
}
/// Tests whether the forest is empty.
pub fn is_empty(&self) -> bool {
- self.root_ids.is_empty()
+ match self {
+ Empty => true,
+ Single(..) | Multiple(..) => false,
+ }
+ }
+
+ /// Iterate over the set of roots.
+ fn iter(&self) -> impl Iterator<Item = DefId> + '_ {
+ self.as_slice().iter().copied()
}
/// Tests whether the forest contains a given DefId.
pub fn contains(&self, tcx: TyCtxt<'tcx>, id: DefId) -> bool {
- self.root_ids.iter().any(|root_id| tcx.is_descendant_of(id, *root_id))
+ slice_contains(tcx, self.as_slice(), id)
}
/// Calculate the intersection of a collection of forests.
I: IntoIterator<Item = DefIdForest>,
{
let mut iter = iter.into_iter();
- let mut ret = if let Some(first) = iter.next() {
- first
+ let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() {
+ SmallVec::from_slice(first.as_slice())
} else {
return DefIdForest::full(tcx);
};
- let mut next_ret = SmallVec::new();
- let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new();
+ let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
for next_forest in iter {
// No need to continue if the intersection is already empty.
- if ret.is_empty() {
- break;
+ if ret.is_empty() || next_forest.is_empty() {
+ return DefIdForest::empty();
}
- for id in ret.root_ids.drain(..) {
- if next_forest.contains(tcx, id) {
- next_ret.push(id);
- } else {
- old_ret.push(id);
- }
- }
- ret.root_ids.extend(old_ret.drain(..));
+ // We keep the elements in `ret` that are also in `next_forest`.
+ next_ret.extend(ret.iter().copied().filter(|&id| next_forest.contains(tcx, id)));
+ // We keep the elements in `next_forest` that are also in `ret`.
+ next_ret.extend(next_forest.iter().filter(|&id| slice_contains(tcx, &ret, id)));
- next_ret.extend(next_forest.root_ids.into_iter().filter(|&id| ret.contains(tcx, id)));
-
- mem::swap(&mut next_ret, &mut ret.root_ids);
- next_ret.drain(..);
+ mem::swap(&mut next_ret, &mut ret);
+ next_ret.clear();
}
- ret
+ DefIdForest::from_slice(&ret)
}
/// Calculate the union of a collection of forests.
where
I: IntoIterator<Item = DefIdForest>,
{
- let mut ret = DefIdForest::empty();
- let mut next_ret = SmallVec::new();
+ let mut ret: SmallVec<[_; 1]> = SmallVec::new();
+ let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
for next_forest in iter {
- next_ret.extend(ret.root_ids.drain(..).filter(|&id| !next_forest.contains(tcx, id)));
+ // Union with the empty set is a no-op.
+ if next_forest.is_empty() {
+ continue;
+ }
- for id in next_forest.root_ids {
- if !next_ret.contains(&id) {
+ // We add everything in `ret` that is not in `next_forest`.
+ next_ret.extend(ret.iter().copied().filter(|&id| !next_forest.contains(tcx, id)));
+ // We add everything in `next_forest` that we haven't added yet.
+ for id in next_forest.iter() {
+ if !slice_contains(tcx, &next_ret, id) {
next_ret.push(id);
}
}
- mem::swap(&mut next_ret, &mut ret.root_ids);
- next_ret.drain(..);
+ mem::swap(&mut next_ret, &mut ret);
+ next_ret.clear();
}
- ret
+ DefIdForest::from_slice(&ret)
}
}
use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef};
use crate::ty::{AdtKind, Visibility};
use crate::ty::{DefId, SubstsRef};
-use rustc_data_structures::stack::ensure_sufficient_stack;
mod def_id_forest;
impl<'tcx> TyS<'tcx> {
/// Calculates the forest of `DefId`s from which this type is visibly uninhabited.
- fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest {
- match *self.kind() {
- Adt(def, substs) => {
- ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env))
- }
+ fn uninhabited_from(
+ &'tcx self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> DefIdForest {
+ tcx.type_uninhabited_from(param_env.and(self))
+ }
+}
- Never => DefIdForest::full(tcx),
+// Query provider for `type_uninhabited_from`.
+pub(crate) fn type_uninhabited_from<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> DefIdForest {
+ let ty = key.value;
+ let param_env = key.param_env;
+ match *ty.kind() {
+ Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env),
- Tuple(ref tys) => DefIdForest::union(
- tcx,
- tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)),
- ),
+ Never => DefIdForest::full(tcx),
- Array(ty, len) => match len.try_eval_usize(tcx, param_env) {
- Some(0) | None => DefIdForest::empty(),
- // If the array is definitely non-empty, it's uninhabited if
- // the type of its elements is uninhabited.
- Some(1..) => ty.uninhabited_from(tcx, param_env),
- },
+ Tuple(ref tys) => DefIdForest::union(
+ tcx,
+ tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)),
+ ),
- // References to uninitialised memory are valid for any type, including
- // uninhabited types, in unsafe code, so we treat all references as
- // inhabited.
- // The precise semantics of inhabitedness with respect to references is currently
- // undecided.
- Ref(..) => DefIdForest::empty(),
+ Array(ty, len) => match len.try_eval_usize(tcx, param_env) {
+ Some(0) | None => DefIdForest::empty(),
+ // If the array is definitely non-empty, it's uninhabited if
+ // the type of its elements is uninhabited.
+ Some(1..) => ty.uninhabited_from(tcx, param_env),
+ },
- _ => DefIdForest::empty(),
- }
+ // References to uninitialised memory are valid for any type, including
+ // uninhabited types, in unsafe code, so we treat all references as
+ // inhabited.
+ // The precise semantics of inhabitedness with respect to references is currently
+ // undecided.
+ Ref(..) => DefIdForest::empty(),
+
+ _ => DefIdForest::empty(),
}
}
impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|value| PredicateAtom::RegionOutlives(value))
+ self.map_bound(PredicateAtom::RegionOutlives)
.potentially_quantified(tcx, PredicateKind::ForAll)
}
}
impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|value| PredicateAtom::TypeOutlives(value))
+ self.map_bound(PredicateAtom::TypeOutlives)
.potentially_quantified(tcx, PredicateKind::ForAll)
}
}
impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|value| PredicateAtom::Projection(value))
- .potentially_quantified(tcx, PredicateKind::ForAll)
+ self.map_bound(PredicateAtom::Projection).potentially_quantified(tcx, PredicateKind::ForAll)
}
}
/// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
match instance {
- ty::InstanceDef::Item(def) => self.optimized_mir_opt_const_arg(def),
+ ty::InstanceDef::Item(def) => match self.def_kind(def.did) {
+ DefKind::Const
+ | DefKind::Static
+ | DefKind::AssocConst
+ | DefKind::Ctor(..)
+ | DefKind::AnonConst => self.mir_for_ctfe_opt_const_arg(def),
+ // If the caller wants `mir_for_ctfe` of a function they should not be using
+ // `instance_mir`, so we'll assume const fn also wants the optimized version.
+ _ => self.optimized_mir_or_const_arg_mir(def),
+ },
ty::InstanceDef::VtableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::Intrinsic(..)
*providers = ty::query::Providers {
trait_impls_of: trait_def::trait_impls_of_provider,
all_local_trait_impls: trait_def::all_local_trait_impls,
+ type_uninhabited_from: inhabitedness::type_uninhabited_from,
..*providers
};
}
use crate::dep_graph::{DepNode, DepNodeIndex, SerializedDepNodeIndex};
use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState};
use crate::mir::{self, interpret};
-use crate::ty::codec::{OpaqueEncoder, RefDecodable, TyDecoder, TyEncoder};
+use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder};
use crate::ty::context::TyCtxt;
use crate::ty::{self, Ty};
use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder, FingerprintEncoder};
use rustc_hir::definitions::DefPathHash;
use rustc_hir::definitions::Definitions;
use rustc_index::vec::{Idx, IndexVec};
-use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder};
+use rustc_serialize::{
+ opaque::{self, FileEncodeResult, FileEncoder},
+ Decodable, Decoder, Encodable, Encoder,
+};
use rustc_session::{CrateDisambiguator, Session};
use rustc_span::hygiene::{
ExpnDataDecodeMode, ExpnDataEncodeMode, ExpnId, HygieneDecodeContext, HygieneEncodeContext,
}
}
- pub fn serialize<'tcx, E>(&self, tcx: TyCtxt<'tcx>, encoder: &mut E) -> Result<(), E::Error>
- where
- E: OpaqueEncoder,
- {
+ pub fn serialize<'tcx>(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ encoder: &mut FileEncoder,
+ ) -> FileEncodeResult {
// Serializing the `DepGraph` should not modify it.
tcx.dep_graph.with_ignore(|| {
// Allocate `SourceFileIndex`es.
// Encode query results.
let mut query_result_index = EncodedQueryResultIndex::new();
- tcx.sess.time("encode_query_results", || {
+ tcx.sess.time("encode_query_results", || -> FileEncodeResult {
let enc = &mut encoder;
let qri = &mut query_result_index;
macro_rules! encode_queries {
($($query:ident,)*) => {
$(
- encode_query_results::<ty::query::queries::$query<'_>, _>(
+ encode_query_results::<ty::query::queries::$query<'_>>(
tcx,
enc,
qri
.current_diagnostics
.borrow()
.iter()
- .map(|(dep_node_index, diagnostics)| {
- let pos = AbsoluteBytePos::new(encoder.position());
- // Let's make sure we get the expected type here.
- let diagnostics: &EncodedDiagnostics = diagnostics;
- let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index());
- encoder.encode_tagged(dep_node_index, diagnostics)?;
-
- Ok((dep_node_index, pos))
- })
+ .map(
+ |(dep_node_index, diagnostics)| -> Result<_, <FileEncoder as Encoder>::Error> {
+ let pos = AbsoluteBytePos::new(encoder.position());
+ // Let's make sure we get the expected type here.
+ let diagnostics: &EncodedDiagnostics = diagnostics;
+ let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index());
+ encoder.encode_tagged(dep_node_index, diagnostics)?;
+
+ Ok((dep_node_index, pos))
+ },
+ )
.collect::<Result<_, _>>()?;
let interpret_alloc_index = {
hygiene_encode_context.encode(
&mut encoder,
- |encoder, index, ctxt_data| {
+ |encoder, index, ctxt_data| -> FileEncodeResult {
let pos = AbsoluteBytePos::new(encoder.position());
encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data)?;
syntax_contexts.insert(index, pos);
Ok(())
},
- |encoder, index, expn_data| {
+ |encoder, index, expn_data| -> FileEncodeResult {
let pos = AbsoluteBytePos::new(encoder.position());
encoder.encode_tagged(TAG_EXPN_DATA, expn_data)?;
expn_ids.insert(index, pos);
// Encode the position of the footer as the last 8 bytes of the
// file so we know where to look for it.
- IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder.opaque())?;
+ IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder)?;
// DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address
// of the footer must be the last thing in the data stream.
//- ENCODING -------------------------------------------------------------------
+trait OpaqueEncoder: Encoder {
+ fn position(&self) -> usize;
+}
+
+impl OpaqueEncoder for FileEncoder {
+ #[inline]
+ fn position(&self) -> usize {
+ FileEncoder::position(self)
+ }
+}
+
/// An encoder that can write to the incremental compilation cache.
struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> {
tcx: TyCtxt<'tcx>,
}
}
-impl<'a, 'tcx> FingerprintEncoder for CacheEncoder<'a, 'tcx, rustc_serialize::opaque::Encoder> {
- fn encode_fingerprint(&mut self, f: &Fingerprint) -> opaque::EncodeResult {
- f.encode_opaque(self.encoder)
+impl<'a, 'tcx, E: OpaqueEncoder> FingerprintEncoder for CacheEncoder<'a, 'tcx, E> {
+ fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), E::Error> {
+ self.encoder.encode_fingerprint(f)
}
}
const CLEAR_CROSS_CRATE: bool = false;
fn position(&self) -> usize {
- self.encoder.encoder_position()
+ self.encoder.position()
}
fn type_shorthands(&mut self) -> &mut FxHashMap<Ty<'tcx>, usize> {
&mut self.type_shorthands
}
}
-// This ensures that the `Encodable<opaque::Encoder>::encode` specialization for byte slices
-// is used when a `CacheEncoder` having an `opaque::Encoder` is passed to `Encodable::encode`.
+// This ensures that the `Encodable<opaque::FileEncoder>::encode` specialization for byte slices
+// is used when a `CacheEncoder` having an `opaque::FileEncoder` is passed to `Encodable::encode`.
// Unfortunately, we have to manually opt into specializations this way, given how `CacheEncoder`
// and the encoding traits currently work.
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx, opaque::Encoder>> for [u8] {
- fn encode(&self, e: &mut CacheEncoder<'a, 'tcx, opaque::Encoder>) -> opaque::EncodeResult {
+impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx, FileEncoder>> for [u8] {
+ fn encode(&self, e: &mut CacheEncoder<'a, 'tcx, FileEncoder>) -> FileEncodeResult {
self.encode(e.encoder)
}
}
pub const ENCODED_SIZE: usize = 8;
}
-impl Encodable<opaque::Encoder> for IntEncodedWithFixedSize {
- fn encode(&self, e: &mut opaque::Encoder) -> Result<(), !> {
+impl<E: OpaqueEncoder> Encodable<E> for IntEncodedWithFixedSize {
+ fn encode(&self, e: &mut E) -> Result<(), E::Error> {
let start_pos = e.position();
for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE {
((self.0 >> (i * 8)) as u8).encode(e)?;
}
}
-fn encode_query_results<'a, 'tcx, Q, E>(
+fn encode_query_results<'a, 'tcx, Q>(
tcx: TyCtxt<'tcx>,
- encoder: &mut CacheEncoder<'a, 'tcx, E>,
+ encoder: &mut CacheEncoder<'a, 'tcx, FileEncoder>,
query_result_index: &mut EncodedQueryResultIndex,
-) -> Result<(), E::Error>
+) -> FileEncodeResult
where
Q: super::QueryDescription<TyCtxt<'tcx>> + super::QueryAccessors<TyCtxt<'tcx>>,
- Q::Value: Encodable<CacheEncoder<'a, 'tcx, E>>,
- E: 'a + OpaqueEncoder,
+ Q::Value: Encodable<CacheEncoder<'a, 'tcx, FileEncoder>>,
{
let _timer = tcx
.sess
// Record position of the cache entry.
query_result_index
- .push((dep_node, AbsoluteBytePos::new(encoder.encoder.opaque().position())));
+ .push((dep_node, AbsoluteBytePos::new(encoder.encoder.position())));
// Encode the type check tables with the `SerializedDepNodeIndex`
// as tag.
let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
+ let loop_message = if location == move_out.source || move_site.traversed_back_edge {
+ ", in previous iteration of loop"
+ } else {
+ ""
+ };
+
if location == move_out.source {
- err.span_label(
- span,
- format!(
- "value {}moved{} here, in previous iteration of loop",
- partially_str, move_msg
- ),
- );
is_loop_move = true;
- } else if move_site.traversed_back_edge {
- err.span_label(
- move_span,
- format!(
- "value {}moved{} here, in previous iteration of loop",
- partially_str, move_msg
- ),
- );
- } else {
- if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } =
- move_spans
- {
- let place_name = self
- .describe_place(moved_place.as_ref())
- .map(|n| format!("`{}`", n))
- .unwrap_or_else(|| "value".to_owned());
- match kind {
- FnSelfUseKind::FnOnceCall => {
+ }
+
+ if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
+ let place_name = self
+ .describe_place(moved_place.as_ref())
+ .map(|n| format!("`{}`", n))
+ .unwrap_or_else(|| "value".to_owned());
+ match kind {
+ FnSelfUseKind::FnOnceCall => {
+ err.span_label(
+ fn_call_span,
+ &format!(
+ "{} {}moved due to this call{}",
+ place_name, partially_str, loop_message
+ ),
+ );
+ err.span_note(
+ var_span,
+ "this value implements `FnOnce`, which causes it to be moved when called",
+ );
+ }
+ FnSelfUseKind::Operator { self_arg } => {
+ err.span_label(
+ fn_call_span,
+ &format!(
+ "{} {}moved due to usage in operator{}",
+ place_name, partially_str, loop_message
+ ),
+ );
+ if self.fn_self_span_reported.insert(fn_span) {
+ err.span_note(
+ self_arg.span,
+ "calling this operator moves the left-hand side",
+ );
+ }
+ }
+ FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
+ if implicit_into_iter {
err.span_label(
fn_call_span,
&format!(
- "{} {}moved due to this call",
- place_name, partially_str
+ "{} {}moved due to this implicit call to `.into_iter()`{}",
+ place_name, partially_str, loop_message
),
);
- err.span_note(
- var_span,
- "this value implements `FnOnce`, which causes it to be moved when called",
- );
- }
- FnSelfUseKind::Operator { self_arg } => {
+ } else {
err.span_label(
fn_call_span,
&format!(
- "{} {}moved due to usage in operator",
- place_name, partially_str
+ "{} {}moved due to this method call{}",
+ place_name, partially_str, loop_message
),
);
- if self.fn_self_span_reported.insert(fn_span) {
- err.span_note(
- self_arg.span,
- "calling this operator moves the left-hand side",
- );
- }
}
- FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
- if implicit_into_iter {
- err.span_label(
- fn_call_span,
- &format!(
- "{} {}moved due to this implicit call to `.into_iter()`",
- place_name, partially_str
- ),
- );
- } else {
- err.span_label(
- fn_call_span,
- &format!(
- "{} {}moved due to this method call",
- place_name, partially_str
- ),
- );
- }
- // Avoid pointing to the same function in multiple different
- // error messages
- if self.fn_self_span_reported.insert(self_arg.span) {
- err.span_note(
- self_arg.span,
- &format!("this function consumes the receiver `self` by taking ownership of it, which moves {}", place_name)
- );
- }
+ // Avoid pointing to the same function in multiple different
+ // error messages
+ if self.fn_self_span_reported.insert(self_arg.span) {
+ err.span_note(
+ self_arg.span,
+ &format!("this function takes ownership of the receiver `self`, which moves {}", place_name)
+ );
}
- // Deref::deref takes &self, which cannot cause a move
- FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
}
- } else {
- err.span_label(
- move_span,
- format!("value {}moved{} here", partially_str, move_msg),
- );
+ // Deref::deref takes &self, which cannot cause a move
+ FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
+ }
+ } else {
+ err.span_label(
+ move_span,
+ format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
+ );
+ // If the move error occurs due to a loop, don't show
+ // another message for the same span
+ if loop_message.is_empty() {
move_spans.var_span_label(
&mut err,
format!(
);
}
}
+
if let UseSpans::PatUse(span) = move_spans {
err.span_suggestion_verbose(
span.shrink_to_lo(),
type MemoryExtra = MemoryExtra;
+ fn load_mir(
+ ecx: &InterpCx<'mir, 'tcx, Self>,
+ instance: ty::InstanceDef<'tcx>,
+ ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
+ match instance {
+ ty::InstanceDef::Item(def) => {
+ if ecx.tcx.is_ctfe_mir_available(def.did) {
+ Ok(ecx.tcx.mir_for_ctfe_opt_const_arg(def))
+ } else {
+ throw_unsup!(NoMirFor(def.did))
+ }
+ }
+ _ => Ok(ecx.tcx.instance_mir(instance)),
+ }
+ }
+
fn find_mir_or_eval_fn(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
if let Some(promoted) = promoted {
return Ok(&self.tcx.promoted_mir_opt_const_arg(def)[promoted]);
}
- match instance {
- ty::InstanceDef::Item(def) => {
- if self.tcx.is_mir_available(def.did) {
- Ok(self.tcx.optimized_mir_opt_const_arg(def))
- } else {
- throw_unsup!(NoMirFor(def.did))
- }
- }
- _ => Ok(self.tcx.instance_mir(instance)),
- }
+ M::load_mir(self, instance)
}
/// Call this on things you got out of the MIR (so it is as generic as the current
/// Whether to enforce the validity invariant
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+ /// Entry point for obtaining the MIR of anything that should get evaluated.
+ /// So not just functions and shims, but also const/static initializers, anonymous
+ /// constants, ...
+ fn load_mir(
+ ecx: &InterpCx<'mir, 'tcx, Self>,
+ instance: ty::InstanceDef<'tcx>,
+ ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
+ Ok(ecx.tcx.instance_mir(instance))
+ }
+
/// Entry point to all function calls.
///
/// Returns either the mir to use for the call, or `None` if execution should
//! generic parameters are unused (and eventually, in what ways generic parameters are used - only
//! for their size, offset of a field, etc.).
-use rustc_hir::{def::DefKind, def_id::DefId};
+use rustc_hir::{def::DefKind, def_id::DefId, ConstContext};
use rustc_index::bit_set::FiniteBitSet;
use rustc_middle::mir::{
visit::{TyContext, Visitor},
}
// Exit early when there is no MIR available.
- if !tcx.is_mir_available(def_id) {
- debug!("unused_generic_params: (no mir available) def_id={:?}", def_id);
- return FiniteBitSet::new_empty();
+ let context = tcx.hir().body_const_context(def_id.expect_local());
+ match context {
+ Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => {
+ debug!("unused_generic_params: (no mir available) def_id={:?}", def_id);
+ return FiniteBitSet::new_empty();
+ }
+ Some(_) if !tcx.is_ctfe_mir_available(def_id) => {
+ debug!("unused_generic_params: (no ctfe mir available) def_id={:?}", def_id);
+ return FiniteBitSet::new_empty();
+ }
+ _ => {}
}
// Create a bitset with N rightmost ones for each parameter.
debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters);
// Visit MIR and accumululate used generic parameters.
- let body = tcx.optimized_mir(def_id);
+ let body = match context {
+ // Const functions are actually called and should thus be considered for polymorphization
+ // via their runtime MIR
+ Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id),
+ Some(_) => tcx.mir_for_ctfe(def_id),
+ };
let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters };
vis.visit_body(body);
debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters);
type MemoryExtra = ();
+ fn load_mir(
+ _ecx: &InterpCx<'mir, 'tcx, Self>,
+ _instance: ty::InstanceDef<'tcx>,
+ ) -> InterpResult<'tcx, &'tcx Body<'tcx>> {
+ throw_machine_stop_str!("calling functions isn't supported in ConstProp")
+ }
+
fn find_mir_or_eval_fn(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
pub(super) fn debug_options<'a>() -> &'a DebugOptions {
static DEBUG_OPTIONS: SyncOnceCell<DebugOptions> = SyncOnceCell::new();
- &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env())
+ &DEBUG_OPTIONS.get_or_init(DebugOptions::from_env)
}
/// Parses and maintains coverage-specific debug options captured from the environment variable
{
bcb_to_coverage_spans_with_counters
.entry(bcb)
- .or_insert_with(|| Vec::new())
+ .or_insert_with(Vec::new)
.push((coverage_span.clone(), counter_kind.clone()));
}
}
if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() {
bcb_to_dependency_counters
.entry(bcb)
- .or_insert_with(|| Vec::new())
+ .or_insert_with(Vec::new)
.push(counter_kind.clone());
}
}
pub fn add_expression_operands(&mut self, expression: &CoverageKind) {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() {
if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression {
- used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id);
- used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id);
+ used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id);
+ used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id);
}
}
}
let operand = counter_kind.as_operand_id();
if let Some(replaced) = self
.edge_from_bcbs
- .get_or_insert_with(|| FxHashMap::default())
+ .get_or_insert_with(FxHashMap::default)
.insert(from_bcb, counter_kind)
{
Error::from_string(format!(
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, Coverage, CoverageInfo, Location};
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefId;
/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each
}
fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
- let mir_body = tcx.optimized_mir(def_id);
+ let mir_body = mir_body(tcx, def_id);
let mut coverage_visitor = CoverageVisitor {
// num_counters always has at least the `ZERO` counter.
}
fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Symbol> {
- let mir_body = tcx.optimized_mir(def_id);
- for bb_data in mir_body.basic_blocks().iter() {
+ for bb_data in mir_body(tcx, def_id).basic_blocks().iter() {
for statement in bb_data.statements.iter() {
if let StatementKind::Coverage(box ref coverage) = statement.kind {
if let Some(code_region) = coverage.code_region.as_ref() {
None
}
+/// This function ensures we obtain the correct MIR for the given item irrespective of
+/// whether that means const mir or runtime mir. For `const fn` this opts for runtime
+/// mir.
+fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> {
+ let id = ty::WithOptConstParam::unknown(def_id);
+ let def = ty::InstanceDef::Item(id);
+ tcx.instance_mir(def)
+}
+
fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> {
- let mir_body: &'tcx mir::Body<'tcx> = tcx.optimized_mir(def_id);
- mir_body
+ mir_body(tcx, def_id)
.basic_blocks()
.iter()
.map(|data| {
},
mir_promoted,
mir_drops_elaborated_and_const_checked,
+ mir_for_ctfe,
+ mir_for_ctfe_of_const_arg,
optimized_mir,
- optimized_mir_of_const_arg,
is_mir_available,
+ is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did),
promoted_mir: |tcx, def_id| {
let def_id = def_id.expect_local();
if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
tcx.alloc_steal_mir(body)
}
+/// Compute the main MIR body and the list of MIR bodies of the promoteds.
fn mir_promoted(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
}
+/// Compute the MIR that is used during CTFE (and thus has no optimizations run on it)
+fn mir_for_ctfe<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Body<'tcx> {
+ let did = def_id.expect_local();
+ if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
+ tcx.mir_for_ctfe_of_const_arg(def)
+ } else {
+ tcx.arena.alloc(inner_mir_for_ctfe(tcx, ty::WithOptConstParam::unknown(did)))
+ }
+}
+
+/// Same as `mir_for_ctfe`, but used to get the MIR of a const generic parameter.
+/// The docs on `WithOptConstParam` explain this a bit more, but the TLDR is that
+/// we'd get cycle errors with `mir_for_ctfe`, because typeck would need to typeck
+/// the const parameter while type checking the main body, which in turn would try
+/// to type check the main body again.
+fn mir_for_ctfe_of_const_arg<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ (did, param_did): (LocalDefId, DefId),
+) -> &'tcx Body<'tcx> {
+ tcx.arena.alloc(inner_mir_for_ctfe(
+ tcx,
+ ty::WithOptConstParam { did, const_param_did: Some(param_did) },
+ ))
+}
+
+fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_> {
+ // FIXME: don't duplicate this between the optimized_mir/mir_for_ctfe queries
+ if tcx.is_constructor(def.did.to_def_id()) {
+ // There's no reason to run all of the MIR passes on constructors when
+ // we can just output the MIR we want directly. This also saves const
+ // qualification and borrow checking the trouble of special casing
+ // constructors.
+ return shim::build_adt_ctor(tcx, def.did.to_def_id());
+ }
+
+ let context = tcx
+ .hir()
+ .body_const_context(def.did)
+ .expect("mir_for_ctfe should not be used for runtime functions");
+
+ let mut body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
+
+ match context {
+ // Do not const prop functions, either they get executed at runtime or exported to metadata,
+ // so we run const prop on them, or they don't, in which case we const evaluate some control
+ // flow paths of the function and any errors in those paths will get emitted as const eval
+ // errors.
+ hir::ConstContext::ConstFn => {}
+ // Static items always get evaluated, so we can just let const eval see if any erroneous
+ // control flow paths get executed.
+ hir::ConstContext::Static(_) => {}
+ // Associated constants get const prop run so we detect common failure situations in the
+ // crate that defined the constant.
+ // Technically we want to not run on regular const items, but oli-obk doesn't know how to
+ // conveniently detect that at this point without looking at the HIR.
+ hir::ConstContext::Const => {
+ #[rustfmt::skip]
+ let optimizations: &[&dyn MirPass<'_>] = &[
+ &const_prop::ConstProp,
+ ];
+
+ #[rustfmt::skip]
+ run_passes(
+ tcx,
+ &mut body,
+ MirPhase::Optimization,
+ &[
+ optimizations,
+ ],
+ );
+ }
+ }
+
+ debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE");
+
+ body
+}
+
+/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs
+/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't
+/// end up missing the source MIR due to stealing happening.
fn mir_drops_elaborated_and_const_checked<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
);
}
+/// Optimize the MIR and prepare it for codegen.
fn optimized_mir<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx Body<'tcx> {
let did = did.expect_local();
- if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
- tcx.optimized_mir_of_const_arg(def)
- } else {
- tcx.arena.alloc(inner_optimized_mir(tcx, ty::WithOptConstParam::unknown(did)))
- }
+ assert_eq!(ty::WithOptConstParam::try_lookup(did, tcx), None);
+ tcx.arena.alloc(inner_optimized_mir(tcx, did))
}
-fn optimized_mir_of_const_arg<'tcx>(
- tcx: TyCtxt<'tcx>,
- (did, param_did): (LocalDefId, DefId),
-) -> &'tcx Body<'tcx> {
- tcx.arena.alloc(inner_optimized_mir(
- tcx,
- ty::WithOptConstParam { did, const_param_did: Some(param_did) },
- ))
-}
-
-fn inner_optimized_mir(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_> {
- if tcx.is_constructor(def.did.to_def_id()) {
+fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
+ if tcx.is_constructor(did.to_def_id()) {
// There's no reason to run all of the MIR passes on constructors when
// we can just output the MIR we want directly. This also saves const
// qualification and borrow checking the trouble of special casing
// constructors.
- return shim::build_adt_ctor(tcx, def.did.to_def_id());
+ return shim::build_adt_ctor(tcx, did.to_def_id());
}
- let mut body = tcx.mir_drops_elaborated_and_const_checked(def).steal();
+ match tcx.hir().body_const_context(did) {
+ // Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked`
+ // which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it
+ // computes and caches its result.
+ Some(hir::ConstContext::ConstFn) => tcx.ensure().mir_for_ctfe(did),
+ None => {}
+ Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
+ }
+ let mut body =
+ tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal();
run_optimization_passes(tcx, &mut body);
debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
body
}
+/// Fetch all the promoteds of an item and prepare their MIR bodies to be ready for
+/// constant evaluation once all substitutions become known.
fn promoted_mir<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
for body in &mut promoted {
run_post_borrowck_cleanup_passes(tcx, body);
- run_optimization_passes(tcx, body);
}
debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");
let mut first = true;
for def_id in dump_mir_def_ids(tcx, single) {
- let body = &tcx.optimized_mir(def_id);
-
if first {
first = false;
} else {
writeln!(w)?;
}
- write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
-
- for body in tcx.promoted_mir(def_id) {
- writeln!(w)?;
+ let render_body = |w: &mut dyn Write, body| -> io::Result<()> {
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
+
+ for body in tcx.promoted_mir(def_id) {
+ writeln!(w)?;
+ write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
+ }
+ Ok(())
+ };
+ match tcx.hir().body_const_context(def_id.expect_local()) {
+ None => render_body(w, tcx.optimized_mir(def_id))?,
+ // For `const fn` we want to render the optimized MIR. If you want the mir used in
+ // ctfe, you can dump the MIR after the `Deaggregator` optimization pass.
+ Some(rustc_hir::ConstContext::ConstFn) => {
+ render_body(w, tcx.optimized_mir(def_id))?;
+ writeln!(w)?;
+ writeln!(w, "// MIR FOR CTFE")?;
+ // Do not use `render_body`, as that would render the promoteds again, but these
+ // are shared between mir_for_ctfe and optimized_mir
+ write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?;
+ }
+ Some(_) => render_body(w, tcx.mir_for_ctfe(def_id))?,
}
}
Ok(())
let item_id = cx.tcx.hir().get_parent_node(hir_id);
let item_def_id = cx.tcx.hir().local_def_id(item_id);
let generics = cx.tcx.generics_of(item_def_id);
- let local_def_id = cx.tcx.hir().local_def_id(hir_id);
- let index = generics.param_def_id_to_index[&local_def_id.to_def_id()];
+ let index = generics.param_def_id_to_index[&def_id];
let name = cx.tcx.hir().name(hir_id);
let val = ty::ConstKind::Param(ty::ParamConst::new(index, name));
ExprKind::Literal {
} else {
Async::No
};
- if let Async::Yes { span, .. } = asyncness {
- // Feature-gate `async ||` closures.
- self.sess.gated_spans.gate(sym::async_closure, span);
- }
let capture_clause = self.parse_capture_clause()?;
let decl = self.parse_fn_block_decl()?;
}
};
+ if let Async::Yes { span, .. } = asyncness {
+ // Feature-gate `async ||` closures.
+ self.sess.gated_spans.gate(sym::async_closure, span);
+ }
+
Ok(self.mk_expr(
lo.to(body.span),
ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)),
return;
}
- // Special-case transmutting from `typeof(function)` and
+ // Special-case transmuting from `typeof(function)` and
// `Option<typeof(function)>` to present a clearer error.
let from = unpack_option_like(self.tcx, from);
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) {
_ => report_errors(self, None),
};
- if let PathSource::TraitItem(..) = source {
- } else {
+ if !matches!(source, PathSource::TraitItem(..)) {
// Avoid recording definition of `A::B` in `<T as A>::B::C`.
self.r.record_partial_res(id, partial_res);
}
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
-use rustc_expand::base::SyntaxExtension;
+use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
/// Used for better errors for E0773
enum BuiltinMacroState {
- NotYetSeen(SyntaxExtension),
+ NotYetSeen(SyntaxExtensionKind),
AlreadySeen(Span),
}
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
use rustc_errors::struct_span_err;
-use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension};
+use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand};
+use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::compile_declarative_macro;
use rustc_expand::expand::{AstFragment, Invocation, InvocationKind};
use rustc_feature::is_builtin_attr_name;
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
}
- fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension) {
- if self.builtin_macros.insert(ident.name, BuiltinMacroState::NotYetSeen(ext)).is_some() {
+ fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) {
+ if self.builtin_macros.insert(name, BuiltinMacroState::NotYetSeen(ext)).is_some() {
self.session
- .span_err(ident.span, &format!("built-in macro `{}` was already defined", ident));
+ .diagnostic()
+ .bug(&format!("built-in macro `{}` was already registered", name));
}
}
// while still taking everything else from the source code.
// If we already loaded this builtin macro, give a better error message than 'no such builtin macro'.
match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
- BuiltinMacroState::NotYetSeen(ext) => result.kind = ext.kind,
+ BuiltinMacroState::NotYetSeen(ext) => result.kind = ext,
BuiltinMacroState::AlreadySeen(span) => {
struct_span_err!(
self.session,
+#![macro_use]
+
+macro_rules! max_leb128_len {
+ ($int_ty:ty) => {
+ // The longest LEB128 encoding for an integer uses 7 bits per byte.
+ (std::mem::size_of::<$int_ty>() * 8 + 6) / 7
+ };
+}
+
+// Returns the longest LEB128 encoding of all supported integer types.
+pub const fn max_leb128_len() -> usize {
+ max_leb128_len!(u128)
+}
+
macro_rules! impl_write_unsigned_leb128 {
- ($fn_name:ident, $int_ty:ident) => {
+ ($fn_name:ident, $int_ty:ty) => {
#[inline]
- pub fn $fn_name(out: &mut Vec<u8>, mut value: $int_ty) {
+ pub fn $fn_name(
+ out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len!($int_ty)],
+ mut value: $int_ty,
+ ) -> &[u8] {
+ let mut i = 0;
+
loop {
if value < 0x80 {
- out.push(value as u8);
+ unsafe {
+ *out.get_unchecked_mut(i).as_mut_ptr() = value as u8;
+ }
+
+ i += 1;
break;
} else {
- out.push(((value & 0x7f) | 0x80) as u8);
+ unsafe {
+ *out.get_unchecked_mut(i).as_mut_ptr() = ((value & 0x7f) | 0x80) as u8;
+ }
+
value >>= 7;
+ i += 1;
}
}
+
+ unsafe { ::std::mem::MaybeUninit::slice_assume_init_ref(&out.get_unchecked(..i)) }
}
};
}
impl_write_unsigned_leb128!(write_usize_leb128, usize);
macro_rules! impl_read_unsigned_leb128 {
- ($fn_name:ident, $int_ty:ident) => {
+ ($fn_name:ident, $int_ty:ty) => {
#[inline]
pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
let mut result = 0;
impl_read_unsigned_leb128!(read_u128_leb128, u128);
impl_read_unsigned_leb128!(read_usize_leb128, usize);
-#[inline]
-/// encodes an integer using signed leb128 encoding and stores
-/// the result using a callback function.
-///
-/// The callback `write` is called once for each position
-/// that is to be written to with the byte to be encoded
-/// at that position.
-pub fn write_signed_leb128_to<W>(mut value: i128, mut write: W)
-where
- W: FnMut(u8),
-{
- loop {
- let mut byte = (value as u8) & 0x7f;
- value >>= 7;
- let more =
- !(((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && ((byte & 0x40) != 0)));
-
- if more {
- byte |= 0x80; // Mark this byte to show that more bytes will follow.
- }
+macro_rules! impl_write_signed_leb128 {
+ ($fn_name:ident, $int_ty:ty) => {
+ #[inline]
+ pub fn $fn_name(
+ out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len!($int_ty)],
+ mut value: $int_ty,
+ ) -> &[u8] {
+ let mut i = 0;
+
+ loop {
+ let mut byte = (value as u8) & 0x7f;
+ value >>= 7;
+ let more = !(((value == 0) && ((byte & 0x40) == 0))
+ || ((value == -1) && ((byte & 0x40) != 0)));
- write(byte);
+ if more {
+ byte |= 0x80; // Mark this byte to show that more bytes will follow.
+ }
+
+ unsafe {
+ *out.get_unchecked_mut(i).as_mut_ptr() = byte;
+ }
+
+ i += 1;
+
+ if !more {
+ break;
+ }
+ }
- if !more {
- break;
+ unsafe { ::std::mem::MaybeUninit::slice_assume_init_ref(&out.get_unchecked(..i)) }
}
- }
+ };
}
-#[inline]
-pub fn write_signed_leb128(out: &mut Vec<u8>, value: i128) {
- write_signed_leb128_to(value, |v| out.push(v))
-}
+impl_write_signed_leb128!(write_i16_leb128, i16);
+impl_write_signed_leb128!(write_i32_leb128, i32);
+impl_write_signed_leb128!(write_i64_leb128, i64);
+impl_write_signed_leb128!(write_i128_leb128, i128);
+impl_write_signed_leb128!(write_isize_leb128, isize);
-#[inline]
-pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
- let mut result = 0;
- let mut shift = 0;
- let mut position = start_position;
- let mut byte;
-
- loop {
- byte = data[position];
- position += 1;
- result |= i128::from(byte & 0x7F) << shift;
- shift += 7;
-
- if (byte & 0x80) == 0 {
- break;
- }
- }
+macro_rules! impl_read_signed_leb128 {
+ ($fn_name:ident, $int_ty:ty) => {
+ #[inline]
+ pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
+ let mut result = 0;
+ let mut shift = 0;
+ let mut position = 0;
+ let mut byte;
- if (shift < 64) && ((byte & 0x40) != 0) {
- // sign extend
- result |= -(1 << shift);
- }
+ loop {
+ byte = slice[position];
+ position += 1;
+ result |= <$int_ty>::from(byte & 0x7F) << shift;
+ shift += 7;
- (result, position - start_position)
+ if (byte & 0x80) == 0 {
+ break;
+ }
+ }
+
+ if (shift < <$int_ty>::BITS) && ((byte & 0x40) != 0) {
+ // sign extend
+ result |= (!0 << shift);
+ }
+
+ (result, position)
+ }
+ };
}
+
+impl_read_signed_leb128!(read_i16_leb128, i16);
+impl_read_signed_leb128!(read_i32_leb128, i32);
+impl_read_signed_leb128!(read_i64_leb128, i64);
+impl_read_signed_leb128!(read_i128_leb128, i128);
+impl_read_signed_leb128!(read_isize_leb128, isize);
#![cfg_attr(bootstrap, feature(min_const_generics))]
#![feature(min_specialization)]
#![feature(vec_spare_capacity)]
+#![feature(core_intrinsics)]
+#![feature(int_bits_const)]
+#![feature(maybe_uninit_slice)]
+#![feature(new_uninit)]
#![cfg_attr(test, feature(test))]
#![allow(rustc::internal)]
-use crate::leb128::{self, read_signed_leb128, write_signed_leb128};
+use crate::leb128::{self, max_leb128_len};
use crate::serialize;
use std::borrow::Cow;
+use std::fs::File;
+use std::io::{self, Write};
use std::mem::MaybeUninit;
+use std::path::Path;
use std::ptr;
// -----------------------------------------------------------------------------
self.data
}
+ #[inline]
+ pub fn position(&self) -> usize {
+ self.data.len()
+ }
+
#[inline]
pub fn emit_raw_bytes(&mut self, s: &[u8]) {
self.data.extend_from_slice(s);
}
}
-macro_rules! write_uleb128 {
- ($enc:expr, $value:expr, $fun:ident) => {{
- leb128::$fun(&mut $enc.data, $value);
- Ok(())
- }};
-}
+macro_rules! write_leb128 {
+ ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{
+ const MAX_ENCODED_LEN: usize = max_leb128_len!($int_ty);
+ let old_len = $enc.data.len();
+
+ if MAX_ENCODED_LEN > $enc.data.capacity() - old_len {
+ $enc.data.reserve(MAX_ENCODED_LEN);
+ }
+
+ // SAFETY: The above check and `reserve` ensures that there is enough
+ // room to write the encoded value to the vector's internal buffer.
+ unsafe {
+ let buf = &mut *($enc.data.as_mut_ptr().add(old_len)
+ as *mut [MaybeUninit<u8>; MAX_ENCODED_LEN]);
+ let encoded = leb128::$fun(buf, $value);
+ $enc.data.set_len(old_len + encoded.len());
+ }
-macro_rules! write_sleb128 {
- ($enc:expr, $value:expr) => {{
- write_signed_leb128(&mut $enc.data, $value as i128);
Ok(())
}};
}
#[inline]
fn emit_usize(&mut self, v: usize) -> EncodeResult {
- write_uleb128!(self, v, write_usize_leb128)
+ write_leb128!(self, v, usize, write_usize_leb128)
}
#[inline]
fn emit_u128(&mut self, v: u128) -> EncodeResult {
- write_uleb128!(self, v, write_u128_leb128)
+ write_leb128!(self, v, u128, write_u128_leb128)
}
#[inline]
fn emit_u64(&mut self, v: u64) -> EncodeResult {
- write_uleb128!(self, v, write_u64_leb128)
+ write_leb128!(self, v, u64, write_u64_leb128)
}
#[inline]
fn emit_u32(&mut self, v: u32) -> EncodeResult {
- write_uleb128!(self, v, write_u32_leb128)
+ write_leb128!(self, v, u32, write_u32_leb128)
}
#[inline]
fn emit_u16(&mut self, v: u16) -> EncodeResult {
- write_uleb128!(self, v, write_u16_leb128)
+ write_leb128!(self, v, u16, write_u16_leb128)
}
#[inline]
#[inline]
fn emit_isize(&mut self, v: isize) -> EncodeResult {
- write_sleb128!(self, v)
+ write_leb128!(self, v, isize, write_isize_leb128)
}
#[inline]
fn emit_i128(&mut self, v: i128) -> EncodeResult {
- write_sleb128!(self, v)
+ write_leb128!(self, v, i128, write_i128_leb128)
}
#[inline]
fn emit_i64(&mut self, v: i64) -> EncodeResult {
- write_sleb128!(self, v)
+ write_leb128!(self, v, i64, write_i64_leb128)
}
#[inline]
fn emit_i32(&mut self, v: i32) -> EncodeResult {
- write_sleb128!(self, v)
+ write_leb128!(self, v, i32, write_i32_leb128)
}
#[inline]
fn emit_i16(&mut self, v: i16) -> EncodeResult {
- write_sleb128!(self, v)
+ write_leb128!(self, v, i16, write_i16_leb128)
}
#[inline]
}
}
-impl Encoder {
+pub type FileEncodeResult = Result<(), io::Error>;
+
+// `FileEncoder` encodes data to file via fixed-size buffer.
+//
+// When encoding large amounts of data to a file, using `FileEncoder` may be
+// preferred over using `Encoder` to encode to a `Vec`, and then writing the
+// `Vec` to file, as the latter uses as much memory as there is encoded data,
+// while the former uses the fixed amount of memory allocated to the buffer.
+// `FileEncoder` also has the advantage of not needing to reallocate as data
+// is appended to it, but the disadvantage of requiring more error handling,
+// which has some runtime overhead.
+pub struct FileEncoder {
+ // The input buffer. For adequate performance, we need more control over
+ // buffering than `BufWriter` offers. If `BufWriter` ever offers a raw
+ // buffer access API, we can use it, and remove `buf` and `buffered`.
+ buf: Box<[MaybeUninit<u8>]>,
+ buffered: usize,
+ flushed: usize,
+ file: File,
+}
+
+impl FileEncoder {
+ pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
+ const DEFAULT_BUF_SIZE: usize = 8192;
+ FileEncoder::with_capacity(path, DEFAULT_BUF_SIZE)
+ }
+
+ pub fn with_capacity<P: AsRef<Path>>(path: P, capacity: usize) -> io::Result<Self> {
+ // Require capacity at least as large as the largest LEB128 encoding
+ // here, so that we don't have to check or handle this on every write.
+ assert!(capacity >= max_leb128_len());
+
+ // Require capacity small enough such that some capacity checks can be
+ // done using guaranteed non-overflowing add rather than sub, which
+ // shaves an instruction off those code paths (on x86 at least).
+ assert!(capacity <= usize::MAX - max_leb128_len());
+
+ let file = File::create(path)?;
+
+ Ok(FileEncoder { buf: Box::new_uninit_slice(capacity), buffered: 0, flushed: 0, file })
+ }
+
#[inline]
pub fn position(&self) -> usize {
- self.data.len()
+ // Tracking position this way instead of having a `self.position` field
+ // means that we don't have to update the position on every write call.
+ self.flushed + self.buffered
+ }
+
+ #[inline]
+ pub fn emit_raw_bytes(&mut self, s: &[u8]) -> FileEncodeResult {
+ self.write_all(s)
+ }
+
+ pub fn flush(&mut self) -> FileEncodeResult {
+ // This is basically a copy of `BufWriter::flush`. If `BufWriter` ever
+ // offers a raw buffer access API, we can use it, and remove this.
+
+ /// Helper struct to ensure the buffer is updated after all the writes
+ /// are complete. It tracks the number of written bytes and drains them
+ /// all from the front of the buffer when dropped.
+ struct BufGuard<'a> {
+ buffer: &'a mut [u8],
+ encoder_buffered: &'a mut usize,
+ encoder_flushed: &'a mut usize,
+ flushed: usize,
+ }
+
+ impl<'a> BufGuard<'a> {
+ fn new(
+ buffer: &'a mut [u8],
+ encoder_buffered: &'a mut usize,
+ encoder_flushed: &'a mut usize,
+ ) -> Self {
+ assert_eq!(buffer.len(), *encoder_buffered);
+ Self { buffer, encoder_buffered, encoder_flushed, flushed: 0 }
+ }
+
+ /// The unwritten part of the buffer
+ fn remaining(&self) -> &[u8] {
+ &self.buffer[self.flushed..]
+ }
+
+ /// Flag some bytes as removed from the front of the buffer
+ fn consume(&mut self, amt: usize) {
+ self.flushed += amt;
+ }
+
+ /// true if all of the bytes have been written
+ fn done(&self) -> bool {
+ self.flushed >= *self.encoder_buffered
+ }
+ }
+
+ impl Drop for BufGuard<'_> {
+ fn drop(&mut self) {
+ if self.flushed > 0 {
+ if self.done() {
+ *self.encoder_flushed += *self.encoder_buffered;
+ *self.encoder_buffered = 0;
+ } else {
+ self.buffer.copy_within(self.flushed.., 0);
+ *self.encoder_flushed += self.flushed;
+ *self.encoder_buffered -= self.flushed;
+ }
+ }
+ }
+ }
+
+ let mut guard = BufGuard::new(
+ unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[..self.buffered]) },
+ &mut self.buffered,
+ &mut self.flushed,
+ );
+
+ while !guard.done() {
+ match self.file.write(guard.remaining()) {
+ Ok(0) => {
+ return Err(io::Error::new(
+ io::ErrorKind::WriteZero,
+ "failed to write the buffered data",
+ ));
+ }
+ Ok(n) => guard.consume(n),
+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+ Err(e) => return Err(e),
+ }
+ }
+
+ Ok(())
+ }
+
+ #[inline]
+ fn capacity(&self) -> usize {
+ self.buf.len()
+ }
+
+ #[inline]
+ fn write_one(&mut self, value: u8) -> FileEncodeResult {
+ // We ensure this during `FileEncoder` construction.
+ debug_assert!(self.capacity() >= 1);
+
+ let mut buffered = self.buffered;
+
+ if std::intrinsics::unlikely(buffered >= self.capacity()) {
+ self.flush()?;
+ buffered = 0;
+ }
+
+ // SAFETY: The above check and `flush` ensures that there is enough
+ // room to write the input to the buffer.
+ unsafe {
+ *MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered) = value;
+ }
+
+ self.buffered = buffered + 1;
+
+ Ok(())
+ }
+
+ #[inline]
+ fn write_all(&mut self, buf: &[u8]) -> FileEncodeResult {
+ let capacity = self.capacity();
+ let buf_len = buf.len();
+
+ if std::intrinsics::likely(buf_len <= capacity) {
+ let mut buffered = self.buffered;
+
+ if std::intrinsics::unlikely(buf_len > capacity - buffered) {
+ self.flush()?;
+ buffered = 0;
+ }
+
+ // SAFETY: The above check and `flush` ensures that there is enough
+ // room to write the input to the buffer.
+ unsafe {
+ let src = buf.as_ptr();
+ let dst = MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered);
+ ptr::copy_nonoverlapping(src, dst, buf_len);
+ }
+
+ self.buffered = buffered + buf_len;
+
+ Ok(())
+ } else {
+ self.write_all_unbuffered(buf)
+ }
+ }
+
+ fn write_all_unbuffered(&mut self, mut buf: &[u8]) -> FileEncodeResult {
+ if self.buffered > 0 {
+ self.flush()?;
+ }
+
+ // This is basically a copy of `Write::write_all` but also updates our
+ // `self.flushed`. It's necessary because `Write::write_all` does not
+ // return the number of bytes written when an error is encountered, and
+ // without that, we cannot accurately update `self.flushed` on error.
+ while !buf.is_empty() {
+ match self.file.write(buf) {
+ Ok(0) => {
+ return Err(io::Error::new(
+ io::ErrorKind::WriteZero,
+ "failed to write whole buffer",
+ ));
+ }
+ Ok(n) => {
+ buf = &buf[n..];
+ self.flushed += n;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+ Err(e) => return Err(e),
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Drop for FileEncoder {
+ fn drop(&mut self) {
+ let _result = self.flush();
+ }
+}
+
+macro_rules! file_encoder_write_leb128 {
+ ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{
+ const MAX_ENCODED_LEN: usize = max_leb128_len!($int_ty);
+
+ // We ensure this during `FileEncoder` construction.
+ debug_assert!($enc.capacity() >= MAX_ENCODED_LEN);
+
+ let mut buffered = $enc.buffered;
+
+ // This can't overflow. See assertion in `FileEncoder::with_capacity`.
+ if std::intrinsics::unlikely(buffered + MAX_ENCODED_LEN > $enc.capacity()) {
+ $enc.flush()?;
+ buffered = 0;
+ }
+
+ // SAFETY: The above check and flush ensures that there is enough
+ // room to write the encoded value to the buffer.
+ let buf = unsafe {
+ &mut *($enc.buf.as_mut_ptr().add(buffered) as *mut [MaybeUninit<u8>; MAX_ENCODED_LEN])
+ };
+
+ let encoded = leb128::$fun(buf, $value);
+ $enc.buffered = buffered + encoded.len();
+
+ Ok(())
+ }};
+}
+
+impl serialize::Encoder for FileEncoder {
+ type Error = io::Error;
+
+ #[inline]
+ fn emit_unit(&mut self) -> FileEncodeResult {
+ Ok(())
+ }
+
+ #[inline]
+ fn emit_usize(&mut self, v: usize) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, usize, write_usize_leb128)
+ }
+
+ #[inline]
+ fn emit_u128(&mut self, v: u128) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, u128, write_u128_leb128)
+ }
+
+ #[inline]
+ fn emit_u64(&mut self, v: u64) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, u64, write_u64_leb128)
+ }
+
+ #[inline]
+ fn emit_u32(&mut self, v: u32) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, u32, write_u32_leb128)
+ }
+
+ #[inline]
+ fn emit_u16(&mut self, v: u16) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, u16, write_u16_leb128)
+ }
+
+ #[inline]
+ fn emit_u8(&mut self, v: u8) -> FileEncodeResult {
+ self.write_one(v)
+ }
+
+ #[inline]
+ fn emit_isize(&mut self, v: isize) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, isize, write_isize_leb128)
+ }
+
+ #[inline]
+ fn emit_i128(&mut self, v: i128) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, i128, write_i128_leb128)
+ }
+
+ #[inline]
+ fn emit_i64(&mut self, v: i64) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, i64, write_i64_leb128)
+ }
+
+ #[inline]
+ fn emit_i32(&mut self, v: i32) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, i32, write_i32_leb128)
+ }
+
+ #[inline]
+ fn emit_i16(&mut self, v: i16) -> FileEncodeResult {
+ file_encoder_write_leb128!(self, v, i16, write_i16_leb128)
+ }
+
+ #[inline]
+ fn emit_i8(&mut self, v: i8) -> FileEncodeResult {
+ let as_u8: u8 = unsafe { std::mem::transmute(v) };
+ self.emit_u8(as_u8)
+ }
+
+ #[inline]
+ fn emit_bool(&mut self, v: bool) -> FileEncodeResult {
+ self.emit_u8(if v { 1 } else { 0 })
+ }
+
+ #[inline]
+ fn emit_f64(&mut self, v: f64) -> FileEncodeResult {
+ let as_u64: u64 = v.to_bits();
+ self.emit_u64(as_u64)
+ }
+
+ #[inline]
+ fn emit_f32(&mut self, v: f32) -> FileEncodeResult {
+ let as_u32: u32 = v.to_bits();
+ self.emit_u32(as_u32)
+ }
+
+ #[inline]
+ fn emit_char(&mut self, v: char) -> FileEncodeResult {
+ self.emit_u32(v as u32)
+ }
+
+ #[inline]
+ fn emit_str(&mut self, v: &str) -> FileEncodeResult {
+ self.emit_usize(v.len())?;
+ self.emit_raw_bytes(v.as_bytes())
}
}
}
}
-macro_rules! read_uleb128 {
+macro_rules! read_leb128 {
($dec:expr, $fun:ident) => {{
let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]);
$dec.position += bytes_read;
}};
}
-macro_rules! read_sleb128 {
- ($dec:expr, $t:ty) => {{
- let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position);
- $dec.position += bytes_read;
- Ok(value as $t)
- }};
-}
-
impl<'a> serialize::Decoder for Decoder<'a> {
type Error = String;
#[inline]
fn read_u128(&mut self) -> Result<u128, Self::Error> {
- read_uleb128!(self, read_u128_leb128)
+ read_leb128!(self, read_u128_leb128)
}
#[inline]
fn read_u64(&mut self) -> Result<u64, Self::Error> {
- read_uleb128!(self, read_u64_leb128)
+ read_leb128!(self, read_u64_leb128)
}
#[inline]
fn read_u32(&mut self) -> Result<u32, Self::Error> {
- read_uleb128!(self, read_u32_leb128)
+ read_leb128!(self, read_u32_leb128)
}
#[inline]
fn read_u16(&mut self) -> Result<u16, Self::Error> {
- read_uleb128!(self, read_u16_leb128)
+ read_leb128!(self, read_u16_leb128)
}
#[inline]
#[inline]
fn read_usize(&mut self) -> Result<usize, Self::Error> {
- read_uleb128!(self, read_usize_leb128)
+ read_leb128!(self, read_usize_leb128)
}
#[inline]
fn read_i128(&mut self) -> Result<i128, Self::Error> {
- read_sleb128!(self, i128)
+ read_leb128!(self, read_i128_leb128)
}
#[inline]
fn read_i64(&mut self) -> Result<i64, Self::Error> {
- read_sleb128!(self, i64)
+ read_leb128!(self, read_i64_leb128)
}
#[inline]
fn read_i32(&mut self) -> Result<i32, Self::Error> {
- read_sleb128!(self, i32)
+ read_leb128!(self, read_i32_leb128)
}
#[inline]
fn read_i16(&mut self) -> Result<i16, Self::Error> {
- read_sleb128!(self, i16)
+ read_leb128!(self, read_i16_leb128)
}
#[inline]
#[inline]
fn read_isize(&mut self) -> Result<isize, Self::Error> {
- read_sleb128!(self, isize)
+ read_leb128!(self, read_isize_leb128)
}
#[inline]
}
}
+impl serialize::Encodable<FileEncoder> for [u8] {
+ fn encode(&self, e: &mut FileEncoder) -> FileEncodeResult {
+ serialize::Encoder::emit_usize(e, self.len())?;
+ e.emit_raw_bytes(self)
+ }
+}
+
// Specialize decoding `Vec<u8>`. This specialization also applies to decoding `Box<[u8]>`s, etc.,
// since the default implementations call `decode` to produce a `Vec<u8>` internally.
impl<'a> serialize::Decodable<Decoder<'a>> for Vec<u8> {
+#![feature(int_bits_const)]
+#![feature(maybe_uninit_slice)]
+#![feature(maybe_uninit_uninit_array)]
+
use rustc_serialize::leb128::*;
+use std::mem::MaybeUninit;
macro_rules! impl_test_unsigned_leb128 {
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
#[test]
fn $test_name() {
+ // Test 256 evenly spaced values of integer range,
+ // integer max value, and some "random" numbers.
+ let mut values = Vec::new();
+
+ let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
+ values.extend((0..256).map(|i| $int_ty::MIN + i * increment));
+
+ values.push($int_ty::MAX);
+
+ values.extend(
+ (-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFu64 as $int_ty)),
+ );
+
let mut stream = Vec::new();
- for x in 0..62 {
- $write_fn_name(&mut stream, (3u64 << x) as $int_ty);
+ for &x in &values {
+ let mut buf = MaybeUninit::uninit_array();
+ stream.extend($write_fn_name(&mut buf, x));
}
let mut position = 0;
- for x in 0..62 {
- let expected = (3u64 << x) as $int_ty;
+ for &expected in &values {
let (actual, bytes_read) = $read_fn_name(&stream[position..]);
assert_eq!(expected, actual);
position += bytes_read;
impl_test_unsigned_leb128!(test_u128_leb128, write_u128_leb128, read_u128_leb128, u128);
impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize);
-#[test]
-fn test_signed_leb128() {
- let values: Vec<_> = (-500..500).map(|i| i * 0x12345789ABCDEF).collect();
- let mut stream = Vec::new();
- for &x in &values {
- write_signed_leb128(&mut stream, x);
- }
- let mut pos = 0;
- for &x in &values {
- let (value, bytes_read) = read_signed_leb128(&mut stream, pos);
- pos += bytes_read;
- assert_eq!(x, value);
- }
- assert_eq!(pos, stream.len());
+macro_rules! impl_test_signed_leb128 {
+ ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
+ #[test]
+ fn $test_name() {
+ // Test 256 evenly spaced values of integer range,
+ // integer max value, and some "random" numbers.
+ let mut values = Vec::new();
+
+ let mut value = $int_ty::MIN;
+ let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
+
+ for _ in 0..256 {
+ values.push(value);
+ // The addition in the last loop iteration overflows.
+ value = value.wrapping_add(increment);
+ }
+
+ values.push($int_ty::MAX);
+
+ values.extend(
+ (-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFi64 as $int_ty)),
+ );
+
+ let mut stream = Vec::new();
+
+ for &x in &values {
+ let mut buf = MaybeUninit::uninit_array();
+ stream.extend($write_fn_name(&mut buf, x));
+ }
+
+ let mut position = 0;
+ for &expected in &values {
+ let (actual, bytes_read) = $read_fn_name(&stream[position..]);
+ assert_eq!(expected, actual);
+ position += bytes_read;
+ }
+ assert_eq!(stream.len(), position);
+ }
+ };
}
+
+impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, read_i16_leb128, i16);
+impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, read_i32_leb128, i32);
+impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, read_i64_leb128, i64);
+impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, read_i128_leb128, i128);
+impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, read_isize_leb128, isize);
SymbolManglingVersion, TrimmedDefPaths,
};
use crate::lint;
+ use crate::options::WasiExecModel;
use crate::utils::NativeLibKind;
use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
impl_dep_tracking_hash_via_hash!(Option<RelocModel>);
impl_dep_tracking_hash_via_hash!(Option<CodeModel>);
impl_dep_tracking_hash_via_hash!(Option<TlsModel>);
+ impl_dep_tracking_hash_via_hash!(Option<WasiExecModel>);
impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>);
impl_dep_tracking_hash_via_hash!(Option<RelroLevel>);
impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
pub const parse_tls_model: &str =
"one of supported TLS models (`rustc --print tls-models`)";
pub const parse_target_feature: &str = parse_string;
+ pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
}
#[allow(dead_code)]
None => false,
}
}
+
+ fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool {
+ match v {
+ Some("command") => *slot = Some(WasiExecModel::Command),
+ Some("reactor") => *slot = Some(WasiExecModel::Reactor),
+ _ => return false,
+ }
+ true
+ }
}
) }
"in general, enable more debug printouts (default: no)"),
verify_llvm_ir: bool = (false, parse_bool, [TRACKED],
"verify LLVM IR (default: no)"),
+ wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED],
+ "whether to build a wasi command or reactor"),
// This list is in alphabetical order.
//
// If you add a new option, please update:
- // - src/librustc_interface/tests.rs
+ // - compiler/rustc_interface/src/tests.rs
+}
+
+#[derive(Clone, Hash)]
+pub enum WasiExecModel {
+ Command,
+ Reactor,
}
self.opts.debugging_opts.tls_model.unwrap_or(self.target.tls_model)
}
+ pub fn is_wasi_reactor(&self) -> bool {
+ self.target.options.os == "wasi"
+ && matches!(
+ self.opts.debugging_opts.wasi_exec_model,
+ Some(config::WasiExecModel::Reactor)
+ )
+ }
+
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
// "mcount" function relies on stack pointer.
// See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.
let (lines, multi_byte_chars, non_narrow_chars) =
analyze_source_file($text, BytePos($source_file_start_pos));
- let expected_lines: Vec<BytePos> = $lines.into_iter().map(|pos| BytePos(pos)).collect();
+ let expected_lines: Vec<BytePos> = $lines.into_iter().map(BytePos).collect();
assert_eq!(lines, expected_lines);
fn test_lev_distance() {
use std::char::{from_u32, MAX};
// Test bytelength agnosticity
- for c in (0..MAX as u32).filter_map(|i| from_u32(i)).map(|i| i.to_string()) {
+ for c in (0..MAX as u32).filter_map(from_u32).map(|i| i.to_string()) {
assert_eq!(lev_distance(&c[..], &c[..]), 0);
}
}
pub fn get_source_file(&self, filename: &FileName) -> Option<Lrc<SourceFile>> {
+ // Remap filename before lookup
+ let filename = self.path_mapping().map_filename_prefix(filename).0;
for sf in self.files.borrow().source_files.iter() {
- if *filename == sf.name {
+ if filename == sf.name {
return Some(sf.clone());
}
}
(path, false)
}
+
+ fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
+ match file {
+ FileName::Real(realfile) => {
+ let path = realfile.local_path();
+ let (path, mapped) = self.map_prefix(path.to_path_buf());
+ (FileName::Real(RealFileName::Named(path)), mapped)
+ }
+ other => (other.clone(), false),
+ }
+ }
}
"nvptx64" => nvptx64::compute_abi_info(self),
"hexagon" => hexagon::compute_abi_info(self),
"riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
- "wasm32" if cx.target_spec().os != "emscripten" => {
- wasm32_bindgen_compat::compute_abi_info(self)
- }
- "wasm32" | "asmjs" => wasm32::compute_abi_info(cx, self),
+ "wasm32" => match cx.target_spec().os.as_str() {
+ "emscripten" | "wasi" => wasm32::compute_abi_info(cx, self),
+ _ => wasm32_bindgen_compat::compute_abi_info(self),
+ },
+ "asmjs" => wasm32::compute_abi_info(cx, self),
a => return Err(format!("unrecognized arch \"{}\" in target specification", a)),
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)]
#[derive(HashStable_Generic)]
pub struct Size {
+ // The top 3 bits are ALWAYS zero.
raw: u64,
}
impl Size {
pub const ZERO: Size = Size { raw: 0 };
- #[inline]
+ /// Rounds `bits` up to the next-higher byte boundary, if `bits` is
+ /// is not aligned.
pub fn from_bits(bits: impl TryInto<u64>) -> Size {
let bits = bits.try_into().ok().unwrap();
+
+ #[cold]
+ fn overflow(bits: u64) -> ! {
+ panic!("Size::from_bits({}) has overflowed", bits);
+ }
+
+ // This is the largest value of `bits` that does not cause overflow
+ // during rounding, and guarantees that the resulting number of bytes
+ // cannot cause overflow when multiplied by 8.
+ if bits > 0xffff_ffff_ffff_fff8 {
+ overflow(bits);
+ }
+
// Avoid potential overflow from `bits + 7`.
- Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8)
+ Size { raw: bits / 8 + ((bits % 8) + 7) / 8 }
}
#[inline]
pub fn from_bytes(bytes: impl TryInto<u64>) -> Size {
- Size { raw: bytes.try_into().ok().unwrap() }
+ let bytes: u64 = bytes.try_into().ok().unwrap();
+ Size { raw: bytes }
}
#[inline]
#[inline]
pub fn bits(self) -> u64 {
- self.bytes().checked_mul(8).unwrap_or_else(|| {
- panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes())
- })
+ self.raw << 3
}
#[inline]
//! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc.
//! See <https://dev.gentoo.org/~vapier/crt.txt> for some more details.
//!
-//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi |
-//! |----------------------|------------------------|------------------------|------------------|-------------------|------|
-//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 |
-//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
-//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 |
-//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
-//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
-//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
-//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - |
+//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi |
+//! |----------------------|------------------------|------------------------|------------------|-------------------|--------------|
+//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 |
+//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
+//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 |
+//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
+//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
+//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
+//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - |
+//! | wasi-reactor-exe | N/A | N/A | N/A | N/A | crt1-reactor |
//!
//! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi |
//! |-----------------------|---------------|---------------|----------------|--------|------|
(LinkOutputKind::DynamicPicExe, &["crt1.o"]),
(LinkOutputKind::StaticNoPicExe, &["crt1.o"]),
(LinkOutputKind::StaticPicExe, &["crt1.o"]),
+ (LinkOutputKind::WasiReactorExe, &["crt1-reactor.o"]),
])
}
--- /dev/null
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_unknown_linux_gnu::target();
+ base.cpu = "i386".to_string();
+ base.llvm_target = "i386-unknown-linux-gnu".to_string();
+ base
+}
--- /dev/null
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_unknown_linux_gnu::target();
+ base.cpu = "i486".to_string();
+ base.llvm_target = "i486-unknown-linux-gnu".to_string();
+ base
+}
DynamicDylib,
/// Dynamic library with bundled libc ("statically linked").
StaticDylib,
+ /// WASI module with a lifetime past the _initialize entry point
+ WasiReactorExe,
}
impl LinkOutputKind {
LinkOutputKind::StaticPicExe => "static-pic-exe",
LinkOutputKind::DynamicDylib => "dynamic-dylib",
LinkOutputKind::StaticDylib => "static-dylib",
+ LinkOutputKind::WasiReactorExe => "wasi-reactor-exe",
}
}
"static-pic-exe" => LinkOutputKind::StaticPicExe,
"dynamic-dylib" => LinkOutputKind::DynamicDylib,
"static-dylib" => LinkOutputKind::StaticDylib,
+ "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe,
_ => return None,
})
}
let kind = LinkOutputKind::from_str(&k).ok_or_else(|| {
format!("{}: '{}' is not a valid value for CRT object kind. \
Use '(dynamic,static)-(nopic,pic)-exe' or \
- '(dynamic,static)-dylib'", name, k)
+ '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k)
})?;
let v = v.as_array().ok_or_else(||
if concrete.is_ok() && substs.has_param_types_or_consts() {
match infcx.tcx.def_kind(def.did) {
DefKind::AnonConst => {
- let mir_body = infcx.tcx.optimized_mir_opt_const_arg(def);
+ let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(def);
if mir_body.is_polymorphic {
future_compat_lint();
let OnUnimplementedNote { message, label, note, enclosing_scope } =
self.on_unimplemented_note(trait_ref, obligation);
let have_alt_message = message.is_some() || label.is_some();
- let is_try = self
- .tcx
- .sess
- .source_map()
- .span_to_snippet(span)
- .map(|s| &s == "?")
- .unwrap_or(false);
- let is_from = self.tcx.get_diagnostic_item(sym::from_trait)
- == Some(trait_ref.def_id());
+ let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
let is_unsize =
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
- let (message, note) = if is_try && is_from {
+ let (message, note) = if is_try_conversion {
(
Some(format!(
"`?` couldn't convert the error to `{}`",
))
);
- if is_try && is_from {
+ if is_try_conversion {
let none_error = self
.tcx
.get_diagnostic_item(sym::none_error)
})
});
- let regular_trait_predicates = existential_trait_refs.map(|trait_ref| {
- trait_ref.map_bound(|trait_ref| ty::ExistentialPredicate::Trait(trait_ref))
- });
+ let regular_trait_predicates = existential_trait_refs
+ .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
});
let mut v = regular_trait_predicates
.chain(auto_trait_predicates)
.chain(
- existential_projections
- .map(|x| x.map_bound(|x| ty::ExistentialPredicate::Projection(x))),
+ existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
)
.collect::<SmallVec<[_; 8]>>();
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
{
diag.span_suggestion(
impl_err_span,
- "consider change the type to match the mutability in trait",
+ "consider changing the mutability to match the trait",
trait_err_str,
Applicability::MachineApplicable,
);
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
return;
}
+ self.suggest_no_capture_closure(err, expected, expr_ty);
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
self.suggest_missing_parentheses(err, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
use crate::astconv::AstConv;
use rustc_ast::util::parser::ExprPrecedence;
-use rustc_span::{self, Span};
+use rustc_span::{self, MultiSpan, Span};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
}
}
+ /// When encountering a closure that captures variables, where a FnPtr is expected,
+ /// suggest a non-capturing closure
+ pub(in super::super) fn suggest_no_capture_closure(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
+ if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
+ // Report upto four upvars being captured to reduce the amount error messages
+ // reported back to the user.
+ let spans_and_labels = upvars
+ .iter()
+ .take(4)
+ .map(|(var_hir_id, upvar)| {
+ let var_name = self.tcx.hir().name(*var_hir_id).to_string();
+ let msg = format!("`{}` captured here", var_name);
+ (upvar.span, msg)
+ })
+ .collect::<Vec<_>>();
+
+ let mut multi_span: MultiSpan =
+ spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
+ for (sp, label) in spans_and_labels {
+ multi_span.push_span_label(sp, label);
+ }
+ err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
+ }
+ }
+ }
+
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
&self,
assert_eq!((&['A', 'B', 'C'] as &[_], &[] as &[_]), dq.as_slices());
}
+#[test]
+fn make_contiguous_head_to_end_2() {
+ // Another test case for #79808, taken from #80293.
+
+ let mut dq = VecDeque::from_iter(0..6);
+ dq.pop_front();
+ dq.pop_front();
+ dq.push_back(6);
+ dq.push_back(7);
+ dq.push_back(8);
+ dq.make_contiguous();
+ let collected: Vec<_> = dq.iter().copied().collect();
+ assert_eq!(dq.as_slices(), (&collected[..], &[] as &[_]));
+}
+
#[test]
fn test_remove() {
// This test checks that every single combination of tail position, length, and
///
/// This trait allows for partial equality, for types that do not have a full
/// equivalence relation. For example, in floating point numbers `NaN != NaN`,
-/// so floating point types implement `PartialEq` but not [`Eq`].
+/// so floating point types implement `PartialEq` but not [`trait@Eq`].
///
/// Formally, the equality must be (for all `a`, `b` and `c`):
///
}
}
+ /// Extracts the values from an array of `MaybeUninit` containers.
+ ///
+ /// # Safety
+ ///
+ /// It is up to the caller to guarantee that all elements of the array are
+ /// in an initialized state.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(maybe_uninit_uninit_array)]
+ /// #![feature(maybe_uninit_array_assume_init)]
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array();
+ /// array[0] = MaybeUninit::new(0);
+ /// array[1] = MaybeUninit::new(1);
+ /// array[2] = MaybeUninit::new(2);
+ ///
+ /// // SAFETY: Now safe as we initialised all elements
+ /// let array = unsafe {
+ /// MaybeUninit::array_assume_init(array)
+ /// };
+ ///
+ /// assert_eq!(array, [0, 1, 2]);
+ /// ```
+ #[unstable(feature = "maybe_uninit_array_assume_init", issue = "80908")]
+ #[inline(always)]
+ pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N] {
+ // SAFETY:
+ // * The caller guarantees that all elements of the array are initialized
+ // * `MaybeUninit<T>` and T are guaranteed to have the same layout
+ // * MaybeUnint does not drop, so there are no double-frees
+ // And thus the conversion is safe
+ unsafe {
+ intrinsics::assert_inhabited::<T>();
+ (&array as *const _ as *const [T; N]).read()
+ }
+ }
+
/// Assuming all the elements are initialized, get a slice to them.
///
/// # Safety
///
/// ```
/// let mut v = [1, 0, 3, 0, 5, 6];
- /// // scoped to restrict the lifetime of the borrows
- /// {
- /// let (left, right) = v.split_at_mut(2);
- /// assert_eq!(left, [1, 0]);
- /// assert_eq!(right, [3, 0, 5, 6]);
- /// left[1] = 2;
- /// right[1] = 4;
- /// }
+ /// let (left, right) = v.split_at_mut(2);
+ /// assert_eq!(left, [1, 0]);
+ /// assert_eq!(right, [3, 0, 5, 6]);
+ /// left[1] = 2;
+ /// right[1] = 4;
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#![feature(raw)]
#![feature(sort_internals)]
#![feature(slice_partition_at_index)]
+#![feature(maybe_uninit_uninit_array)]
+#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_write_slice)]
#![feature(min_specialization)]
assert!(TRUE);
}
+#[test]
+fn uninit_array_assume_init() {
+ let mut array: [MaybeUninit<i16>; 5] = MaybeUninit::uninit_array();
+ array[0].write(3);
+ array[1].write(1);
+ array[2].write(4);
+ array[3].write(1);
+ array[4].write(5);
+
+ let array = unsafe { MaybeUninit::array_assume_init(array) };
+
+ assert_eq!(array, [3, 1, 4, 1, 5]);
+}
+
#[test]
fn uninit_write_slice() {
let mut dst = [MaybeUninit::new(255); 64];
// a backtrace or actually symbolizing it.
use crate::backtrace_rs::{self, BytesOrWideString};
+use crate::cell::UnsafeCell;
use crate::env;
use crate::ffi::c_void;
use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use crate::sync::Mutex;
+use crate::sync::Once;
use crate::sys_common::backtrace::{lock, output_filename};
use crate::vec::Vec;
enum Inner {
Unsupported,
Disabled,
- Captured(Mutex<Capture>),
+ Captured(LazilyResolvedCapture),
}
struct Capture {
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut capture = match &self.inner {
+ let capture = match &self.inner {
Inner::Unsupported => return fmt.write_str("<unsupported>"),
Inner::Disabled => return fmt.write_str("<disabled>"),
- Inner::Captured(c) => c.lock().unwrap(),
+ Inner::Captured(c) => c.force(),
};
- capture.resolve();
let frames = &capture.frames[capture.actual_start..];
let inner = if frames.is_empty() {
Inner::Unsupported
} else {
- Inner::Captured(Mutex::new(Capture {
+ Inner::Captured(LazilyResolvedCapture::new(Capture {
actual_start: actual_start.unwrap_or(0),
frames,
resolved: false,
impl fmt::Display for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut capture = match &self.inner {
+ let capture = match &self.inner {
Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
Inner::Disabled => return fmt.write_str("disabled backtrace"),
- Inner::Captured(c) => c.lock().unwrap(),
+ Inner::Captured(c) => c.force(),
};
- capture.resolve();
let full = fmt.alternate();
let (frames, style) = if full {
}
}
+struct LazilyResolvedCapture {
+ sync: Once,
+ capture: UnsafeCell<Capture>,
+}
+
+impl LazilyResolvedCapture {
+ fn new(capture: Capture) -> Self {
+ LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) }
+ }
+
+ fn force(&self) -> &Capture {
+ self.sync.call_once(|| {
+ // SAFETY: This exclusive reference can't overlap with any others
+ // `Once` guarantees callers will block until this closure returns
+ // `Once` also guarantees only a single caller will enter this closure
+ unsafe { &mut *self.capture.get() }.resolve();
+ });
+
+ // SAFETY: This shared reference can't overlap with the exclusive reference above
+ unsafe { &*self.capture.get() }
+ }
+}
+
+// SAFETY: Access to the inner value is synchronized using a thread-safe `Once`
+// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too
+unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {}
+
impl Capture {
fn resolve(&mut self) {
// If we're already resolved, nothing to do!
#[test]
fn test_debug() {
let backtrace = Backtrace {
- inner: Inner::Captured(Mutex::new(Capture {
+ inner: Inner::Captured(LazilyResolvedCapture::new(Capture {
actual_start: 1,
resolved: true,
frames: vec![
\n]";
assert_eq!(format!("{:#?}", backtrace), expected);
+
+ // Format the backtrace a second time, just to make sure lazily resolved state is stable
+ assert_eq!(format!("{:#?}", backtrace), expected);
}
}
} else if target.ends_with("-wasi") {
let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi");
- copy_and_stamp(
- builder,
- &libdir_self_contained,
- &srcdir,
- "crt1.o",
- &mut target_deps,
- DependencyType::TargetSelfContained,
- );
+ for &obj in &["crt1.o", "crt1-reactor.o"] {
+ copy_and_stamp(
+ builder,
+ &libdir_self_contained,
+ &srcdir,
+ obj,
+ &mut target_deps,
+ DependencyType::TargetSelfContained,
+ );
+ }
} else if target.contains("windows-gnu") {
for obj in ["crt2.o", "dllcrt2.o"].iter() {
let src = compiler_file(builder, builder.cc(target), target, obj);
)
}),
unstable("test-builder", |o| {
- o.optflag(
- "",
- "test-builder",
- "specified the rustc-like binary to use as the test builder",
- )
+ o.optopt("", "test-builder", "The rustc-like binary to use as the test builder", "PATH")
}),
unstable("check", |o| o.optflag("", "check", "Run rustdoc checks")),
]
"f64" => F64,
"char" => Char,
"bool" | "true" | "false" => Bool,
- "str" => Str,
+ "str" | "&str" => Str,
// See #80181 for why these don't have symbols associated.
"slice" => Slice,
"array" => Array,
-Subproject commit fb115ee43b77601b237717c21ab0a8f5b5b9d50a
+Subproject commit f9a8d70b6e0365ac2172ca6b7f1de0341297458d
// revisions:rpass1 rpass2
-// compile-flags: -Z query-dep-graph -O
+// compile-flags: -Z query-dep-graph
// aux-build:cached_hygiene.rs
// This tests the folllowing scenario
// the metadata. Specifically, we were not resetting `orig_id`
// for an `EpxnData` generate in the current crate, which would cause
// us to serialize the `ExpnId` pointing to a garbage location in
-// the metadata.o
-
-// NOTE: We're explicitly passing the `-O` optimization flag because if optimizations are not
-// enabled, then rustc will ignore the `#[inline(always)]` attribute which means we do not load
-// the optimized mir for the unmodified function to be loaded and so the CGU containing that
-// function will be reused.
+// the metadata.
#![feature(rustc_attrs)]
// revisions:rpass1 rpass2 rpass3
-// compile-flags: -Z query-dep-graph -g -O
+// compile-flags: -Z query-dep-graph -g
// aux-build:extern_crate.rs
// ignore-asmjs wasm2js does not support source maps yet
-
// This test case makes sure that we detect if paths emitted into debuginfo
// are changed, even when the change happens in an external crate.
-// NOTE: We're explicitly passing the `-O` optimization flag because if no optimizations are
-// requested, rustc will ignore the `#[inline]` attribute. This is a performance optimization for
-// non-optimized builds which causes us to generate fewer copies of inlined functions when
-// runtime performance doesn't matter. Without this flag, the function will go into a different
-// CGU which can be reused by this crate.
-
#![feature(rustc_attrs)]
#![rustc_partition_reused(module="main", cfg="rpass2")]
static Y: i32 = 42;
// EMIT_MIR const_promotion_extern_static.BAR.PromoteTemps.diff
-// EMIT_MIR const_promotion_extern_static.BAR-promoted[0].ConstProp.after.mir
+// EMIT_MIR const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir
static mut BAR: *const &i32 = [&Y].as_ptr();
// EMIT_MIR const_promotion_extern_static.FOO.PromoteTemps.diff
-// EMIT_MIR const_promotion_extern_static.FOO-promoted[0].ConstProp.after.mir
+// EMIT_MIR const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir
static mut FOO: *const &i32 = [unsafe { &X }].as_ptr();
// EMIT_MIR const_promotion_extern_static.BOP.mir_map.0.mir
}
alloc0 (static: FOO, size: 8, align: 4) {
- ╾─alloc14─╼ 03 00 00 00 │ ╾──╼....
+ ╾─alloc17─╼ 03 00 00 00 │ ╾──╼....
}
-alloc14 (size: 48, align: 4) {
+alloc17 (size: 48, align: 4) {
0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼....
- 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc7──╼ 02 00 00 00 │ ....░░░░╾──╼....
- 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc11─╼ 03 00 00 00 │ ....*...╾──╼....
+ 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 02 00 00 00 │ ....░░░░╾──╼....
+ 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc13─╼ 03 00 00 00 │ ....*...╾──╼....
}
alloc4 (size: 0, align: 4) {}
-alloc7 (size: 16, align: 4) {
- ╾─alloc6──╼ 03 00 00 00 ╾─alloc8──╼ 03 00 00 00 │ ╾──╼....╾──╼....
+alloc8 (size: 16, align: 4) {
+ ╾─alloc7──╼ 03 00 00 00 ╾─alloc9──╼ 03 00 00 00 │ ╾──╼....╾──╼....
}
-alloc6 (size: 3, align: 1) {
+alloc7 (size: 3, align: 1) {
66 6f 6f │ foo
}
-alloc8 (size: 3, align: 1) {
+alloc9 (size: 3, align: 1) {
62 61 72 │ bar
}
-alloc11 (size: 24, align: 4) {
- 0x00 │ ╾─alloc10─╼ 03 00 00 00 ╾─alloc12─╼ 03 00 00 00 │ ╾──╼....╾──╼....
- 0x10 │ ╾─alloc13─╼ 04 00 00 00 │ ╾──╼....
+alloc13 (size: 24, align: 4) {
+ 0x00 │ ╾─alloc12─╼ 03 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ╾──╼....╾──╼....
+ 0x10 │ ╾─alloc15─╼ 04 00 00 00 │ ╾──╼....
}
-alloc10 (size: 3, align: 1) {
+alloc12 (size: 3, align: 1) {
6d 65 68 │ meh
}
-alloc12 (size: 3, align: 1) {
+alloc14 (size: 3, align: 1) {
6d 6f 70 │ mop
}
-alloc13 (size: 4, align: 1) {
+alloc15 (size: 4, align: 1) {
6d c3 b6 70 │ m..p
}
}
alloc0 (static: FOO, size: 16, align: 8) {
- ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+ ╾───────alloc17───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
}
-alloc14 (size: 72, align: 8) {
+alloc17 (size: 72, align: 8) {
0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
- 0x20 │ ╾───────alloc7────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
- 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc11───────╼ │ ....*...╾──────╼
+ 0x20 │ ╾───────alloc8────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
+ 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc13───────╼ │ ....*...╾──────╼
0x40 │ 03 00 00 00 00 00 00 00 │ ........
}
alloc4 (size: 0, align: 8) {}
-alloc7 (size: 32, align: 8) {
- 0x00 │ ╾───────alloc6────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
- 0x10 │ ╾───────alloc8────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+alloc8 (size: 32, align: 8) {
+ 0x00 │ ╾───────alloc7────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+ 0x10 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
}
-alloc6 (size: 3, align: 1) {
+alloc7 (size: 3, align: 1) {
66 6f 6f │ foo
}
-alloc8 (size: 3, align: 1) {
+alloc9 (size: 3, align: 1) {
62 61 72 │ bar
}
-alloc11 (size: 48, align: 8) {
- 0x00 │ ╾───────alloc10───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
- 0x10 │ ╾───────alloc12───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
- 0x20 │ ╾───────alloc13───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........
+alloc13 (size: 48, align: 8) {
+ 0x00 │ ╾───────alloc12───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+ 0x10 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+ 0x20 │ ╾───────alloc15───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........
}
-alloc10 (size: 3, align: 1) {
+alloc12 (size: 3, align: 1) {
6d 65 68 │ meh
}
-alloc12 (size: 3, align: 1) {
+alloc14 (size: 3, align: 1) {
6d 6f 70 │ mop
}
-alloc13 (size: 4, align: 1) {
+alloc15 (size: 4, align: 1) {
6d c3 b6 70 │ m..p
}
}
alloc0 (static: FOO, size: 8, align: 4) {
- ╾─alloc20─╼ 03 00 00 00 │ ╾──╼....
+ ╾─alloc27─╼ 03 00 00 00 │ ╾──╼....
}
-alloc20 (size: 48, align: 4) {
- 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 00 00 00 00 │ ....░░░░╾──╼....
- 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc12─╼ 02 00 00 00 │ ....░░░░╾──╼....
- 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc19─╼ 03 00 00 00 │ ....*...╾──╼....
+alloc27 (size: 48, align: 4) {
+ 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc12─╼ 00 00 00 00 │ ....░░░░╾──╼....
+ 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc17─╼ 02 00 00 00 │ ....░░░░╾──╼....
+ 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc25─╼ 03 00 00 00 │ ....*...╾──╼....
}
-alloc8 (size: 0, align: 4) {}
+alloc12 (size: 0, align: 4) {}
-alloc12 (size: 8, align: 4) {
- ╾─alloc10─╼ ╾─alloc11─╼ │ ╾──╼╾──╼
+alloc17 (size: 8, align: 4) {
+ ╾─alloc15─╼ ╾─alloc16─╼ │ ╾──╼╾──╼
}
-alloc10 (size: 1, align: 1) {
+alloc15 (size: 1, align: 1) {
05 │ .
}
-alloc11 (size: 1, align: 1) {
+alloc16 (size: 1, align: 1) {
06 │ .
}
-alloc19 (size: 12, align: 4) {
- ╾─a15+0x3─╼ ╾─alloc16─╼ ╾─a18+0x2─╼ │ ╾──╼╾──╼╾──╼
+alloc25 (size: 12, align: 4) {
+ ╾─a21+0x3─╼ ╾─alloc22─╼ ╾─a24+0x2─╼ │ ╾──╼╾──╼╾──╼
}
-alloc15 (size: 4, align: 1) {
+alloc21 (size: 4, align: 1) {
2a 45 15 6f │ *E.o
}
-alloc16 (size: 1, align: 1) {
+alloc22 (size: 1, align: 1) {
2a │ *
}
-alloc18 (size: 4, align: 1) {
+alloc24 (size: 4, align: 1) {
2a 45 15 6f │ *E.o
}
}
alloc0 (static: FOO, size: 16, align: 8) {
- ╾───────alloc20───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+ ╾───────alloc27───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
}
-alloc20 (size: 72, align: 8) {
- 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc8────────╼ │ ....░░░░╾──────╼
+alloc27 (size: 72, align: 8) {
+ 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc12───────╼ │ ....░░░░╾──────╼
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
- 0x20 │ ╾───────alloc12───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
- 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc19───────╼ │ ....*...╾──────╼
+ 0x20 │ ╾───────alloc17───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
+ 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc25───────╼ │ ....*...╾──────╼
0x40 │ 03 00 00 00 00 00 00 00 │ ........
}
-alloc8 (size: 0, align: 8) {}
+alloc12 (size: 0, align: 8) {}
-alloc12 (size: 16, align: 8) {
- ╾───────alloc10───────╼ ╾───────alloc11───────╼ │ ╾──────╼╾──────╼
+alloc17 (size: 16, align: 8) {
+ ╾───────alloc15───────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼
}
-alloc10 (size: 1, align: 1) {
+alloc15 (size: 1, align: 1) {
05 │ .
}
-alloc11 (size: 1, align: 1) {
+alloc16 (size: 1, align: 1) {
06 │ .
}
-alloc19 (size: 24, align: 8) {
- 0x00 │ ╾─────alloc15+0x3─────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼
- 0x10 │ ╾─────alloc18+0x2─────╼ │ ╾──────╼
+alloc25 (size: 24, align: 8) {
+ 0x00 │ ╾─────alloc21+0x3─────╼ ╾───────alloc22───────╼ │ ╾──────╼╾──────╼
+ 0x10 │ ╾─────alloc24+0x2─────╼ │ ╾──────╼
}
-alloc15 (size: 4, align: 1) {
+alloc21 (size: 4, align: 1) {
2a 45 15 6f │ *E.o
}
-alloc16 (size: 1, align: 1) {
+alloc22 (size: 1, align: 1) {
2a │ *
}
-alloc18 (size: 4, align: 1) {
+alloc24 (size: 4, align: 1) {
2a 45 15 6f │ *E.o
}
}
alloc0 (static: FOO, size: 4, align: 4) {
- ╾─alloc9──╼ │ ╾──╼
+ ╾─alloc10─╼ │ ╾──╼
}
-alloc9 (size: 168, align: 1) {
+alloc10 (size: 168, align: 1) {
0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
- 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc4──╼ │ ............╾──╼
+ 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc5──╼ │ ............╾──╼
0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
- 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc6──╼ 00 00 │ ..........╾──╼..
- 0x90 │ ╾─a7+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
+ 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc7──╼ 00 00 │ ..........╾──╼..
+ 0x90 │ ╾─a8+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
0xa0 │ 00 00 00 00 00 00 00 00 │ ........
}
-alloc4 (size: 4, align: 4) {
+alloc5 (size: 4, align: 4) {
2a 00 00 00 │ *...
}
-alloc6 (fn: main)
+alloc7 (fn: main)
-alloc7 (size: 100, align: 1) {
+alloc8 (size: 100, align: 1) {
0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
}
alloc0 (static: FOO, size: 8, align: 8) {
- ╾───────alloc9────────╼ │ ╾──────╼
+ ╾───────alloc10───────╼ │ ╾──────╼
}
-alloc9 (size: 180, align: 1) {
+alloc10 (size: 180, align: 1) {
0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
- 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc4── │ ............╾───
+ 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc5── │ ............╾───
0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............
0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─
- 0x90 │ ─────alloc6─────╼ 00 00 ╾─────alloc7+0x63─────╼ │ ─────╼..╾──────╼
+ 0x90 │ ─────alloc7─────╼ 00 00 ╾─────alloc8+0x63─────╼ │ ─────╼..╾──────╼
0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0xb0 │ 00 00 00 00 │ ....
}
-alloc4 (size: 4, align: 4) {
+alloc5 (size: 4, align: 4) {
2a 00 00 00 │ *...
}
-alloc6 (fn: main)
+alloc7 (fn: main)
-alloc7 (size: 100, align: 1) {
+alloc8 (size: 100, align: 1) {
0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+++ /dev/null
-// MIR for `BAR::promoted[0]` after ConstProp
-
-promoted[0] in BAR: &[&i32; 1] = {
- let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
- let mut _3: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
-
- bb0: {
- _3 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
- // ty::Const
- // + ty: &i32
- // + val: Value(Scalar(alloc0))
- // mir::Constant
- // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
- // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) }
- _2 = _3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
- _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- }
-}
-
-alloc0 (static: Y, size: 4, align: 4) {
- 2a 00 00 00 │ *...
-}
--- /dev/null
+// MIR for `BAR::promoted[0]` after SimplifyCfg-elaborate-drops
+
+promoted[0] in BAR: &[&i32; 1] = {
+ let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+ let mut _3: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
+
+ bb0: {
+ _3 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
+ // ty::Const
+ // + ty: &i32
+ // + val: Value(Scalar(alloc0))
+ // mir::Constant
+ // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
+ // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) }
+ _2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+ _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ }
+}
+
+alloc0 (static: Y, size: 4, align: 4) {
+ 2a 00 00 00 │ *...
+}
+++ /dev/null
-// MIR for `FOO::promoted[0]` after ConstProp
-
-promoted[0] in FOO: &[&i32; 1] = {
- let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
- let mut _3: *const i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
-
- bb0: {
- _3 = const {alloc2: *const i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
- // ty::Const
- // + ty: *const i32
- // + val: Value(Scalar(alloc2))
- // mir::Constant
- // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
- // + literal: Const { ty: *const i32, val: Value(Scalar(alloc2)) }
- _2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
- _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- }
-}
-
-alloc2 (extern static: X)
--- /dev/null
+// MIR for `FOO::promoted[0]` after SimplifyCfg-elaborate-drops
+
+promoted[0] in FOO: &[&i32; 1] = {
+ let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
+ let mut _3: *const i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
+
+ bb0: {
+ _3 = const {alloc2: *const i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
+ // ty::Const
+ // + ty: *const i32
+ // + val: Value(Scalar(alloc2))
+ // mir::Constant
+ // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
+ // + literal: Const { ty: *const i32, val: Value(Scalar(alloc2)) }
+ _2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
+ _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ }
+}
+
+alloc2 (extern static: X)
--- /dev/null
+#![feature(prelude_import)]
+#![no_std]
+#[prelude_import]
+use ::std::prelude::v1::*;
+#[macro_use]
+extern crate std;
+// Test for issue 80832
+//
+// pretty-mode:expanded
+// pp-exact:expanded-and-path-remap-80832.pp
+// compile-flags: --remap-path-prefix {{src-base}}=the/src
+
+fn main() { }
--- /dev/null
+// Test for issue 80832
+//
+// pretty-mode:expanded
+// pp-exact:expanded-and-path-remap-80832.pp
+// compile-flags: --remap-path-prefix {{src-base}}=the/src
+
+fn main() {}
-include ../tools.mk
all:
- $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 -C opt-level=0
- if ![cat $(TMPDIR)/*.ll | $(CGREP) -e '\bcall\b']; then \
- echo "not found call instruction when one was expected"; \
- exit 1; \
- fi
- $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 -C opt-level=1
+ $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2
if cat $(TMPDIR)/*.ll | $(CGREP) -e '\bcall\b'; then \
echo "found call instruction when one wasn't expected"; \
exit 1; \
--- /dev/null
+-include ../../run-make-fulldeps/tools.mk
+
+all:
+ $(RUSTC) main.rs --emit=mir -o "$(TMPDIR)"/dump.mir
+
+ifdef RUSTC_BLESS_TEST
+ cp "$(TMPDIR)"/dump.mir dump.mir
+else
+ $(DIFF) dump.mir "$(TMPDIR)"/dump.mir
+endif
--- /dev/null
+// WARNING: This output format is intended for human consumers only
+// and is subject to change without notice. Knock yourself out.
+fn main() -> () {
+ let mut _0: (); // return place in scope 0 at main.rs:8:11: 8:11
+ let _1: i32; // in scope 0 at main.rs:9:5: 9:10
+
+ bb0: {
+ StorageLive(_1); // scope 0 at main.rs:9:5: 9:10
+ _1 = foo() -> bb1; // scope 0 at main.rs:9:5: 9:10
+ // mir::Constant
+ // + span: main.rs:9:5: 9:8
+ // + literal: Const { ty: fn() -> i32 {foo}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at main.rs:9:10: 9:11
+ _0 = const (); // scope 0 at main.rs:8:11: 10:2
+ return; // scope 0 at main.rs:10:2: 10:2
+ }
+}
+
+fn foo() -> i32 {
+ let mut _0: i32; // return place in scope 0 at main.rs:4:19: 4:22
+
+ bb0: {
+ _0 = const 11_i32; // scope 0 at main.rs:5:5: 5:10
+ return; // scope 0 at main.rs:6:2: 6:2
+ }
+}
+
+// MIR FOR CTFE
+fn foo() -> i32 {
+ let mut _0: i32; // return place in scope 0 at main.rs:4:19: 4:22
+ let mut _1: (i32, bool); // in scope 0 at main.rs:5:5: 5:10
+
+ bb0: {
+ _1 = CheckedAdd(const 5_i32, const 6_i32); // scope 0 at main.rs:5:5: 5:10
+ assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 5_i32, const 6_i32) -> bb1; // scope 0 at main.rs:5:5: 5:10
+ }
+
+ bb1: {
+ _0 = move (_1.0: i32); // scope 0 at main.rs:5:5: 5:10
+ return; // scope 0 at main.rs:6:2: 6:2
+ }
+}
--- /dev/null
+// emit-mir
+// check-pass
+
+const fn foo() -> i32 {
+ 5 + 6
+}
+
+fn main() {
+ foo();
+}
// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.array.html#method.map"]' 'array::map'
//! [array::map]
+// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html"]' 'owned str'
+// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html"]' 'str ref'
+// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.is_empty"]' 'str::is_empty'
+// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.len"]' '&str::len'
+//! [owned str][str]
+//! [str ref][&str]
+//! [str::is_empty]
+//! [&str::len]
+
// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' 'pointer::is_null'
// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*const::is_null'
// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*mut::is_null'
--- /dev/null
+// compile-flags: --test -Z unstable-options --test-builder true
+
+/// ```no_run
+/// This tests that `--test-builder` is accepted as a flag by rustdoc.
+/// ```
+pub struct Foo;
|
LL | const BAR: u32 = IMPL_REF_BAR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: ...which requires optimizing MIR for `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 13:2>::BAR`...
+note: ...which requires caching mir of `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 13:2>::BAR` for CTFE...
--> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5
|
LL | const BAR: u32 = IMPL_REF_BAR;
|
LL | const BAR: u32 = DEFAULT_REF_BAR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: ...which requires optimizing MIR for `FooDefault::BAR`...
+note: ...which requires caching mir of `FooDefault::BAR` for CTFE...
--> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5
|
LL | const BAR: u32 = DEFAULT_REF_BAR;
|
LL | const BAR: u32 = TRAIT_REF_BAR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: ...which requires optimizing MIR for `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 13:2>::BAR`...
+note: ...which requires caching mir of `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 13:2>::BAR` for CTFE...
--> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5
|
LL | const BAR: u32 = TRAIT_REF_BAR;
--> $DIR/higher-ranked-projection.rs:25:5
|
LL | foo(());
- | ^^^ one type is more general than the other
+ | ^^^ lifetime mismatch
|
= note: expected type `&'a ()`
found type `&()`
+note: the lifetime requirement is introduced here
+ --> $DIR/higher-ranked-projection.rs:15:33
+ |
+LL | where for<'a> &'a T: Mirror<Image=U>
+ | ^^^^^^^
error: aborting due to previous error
|
= note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
= help: add `#![feature(async_closure)]` to the crate attributes to enable
+ = help: to use an async block, remove the `||`: `async {`
error: aborting due to previous error
|
= note: expected fn pointer `fn(u8) -> u8`
found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:50]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+ --> $DIR/closure-no-fn-1.rs:6:39
+ |
+LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+ | ^ `a` captured here
error: aborting due to previous error
|
= note: expected fn pointer `fn() -> u8`
found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:35]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+ --> $DIR/closure-no-fn-2.rs:6:32
+ |
+LL | let bar: fn() -> u8 = || { b };
+ | ^ `b` captured here
error: aborting due to previous error
--- /dev/null
+fn main() {
+ let b = 2;
+ let _: fn(usize) -> usize = match true {
+ true => |a| a + 1,
+ false => |a| a - b,
+ //~^ ERROR `match` arms have incompatible types
+ };
+}
--- /dev/null
+error[E0308]: `match` arms have incompatible types
+ --> $DIR/closure-no-fn-4.rs:5:18
+ |
+LL | let _: fn(usize) -> usize = match true {
+ | _________________________________-
+LL | | true => |a| a + 1,
+ | | --------- this is found to be of type `fn(usize) -> usize`
+LL | | false => |a| a - b,
+ | | ^^^^^^^^^ expected fn pointer, found closure
+LL | |
+LL | | };
+ | |_____- `match` arms have incompatible types
+ |
+ = note: expected fn pointer `fn(usize) -> usize`
+ found closure `[closure@$DIR/closure-no-fn-4.rs:5:18: 5:27]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+ --> $DIR/closure-no-fn-4.rs:5:26
+ |
+LL | false => |a| a - b,
+ | ^ `b` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// When providing diagnostics about not being able to coerce a capturing-closure
+// to fn type, we want to report only upto 4 captures.
+
+fn main() {
+ let a = 0u8;
+ let b = 0u8;
+ let c = 0u8;
+ let d = 0u8;
+ let e = 0u8;
+ let bar: fn() -> u8 = || { a; b; c; d; e };
+ //~^ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/closure-no-fn-5.rs:10:27
+ |
+LL | let bar: fn() -> u8 = || { a; b; c; d; e };
+ | ---------- ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
+ | |
+ | expected due to this
+ |
+ = note: expected fn pointer `fn() -> u8`
+ found closure `[closure@$DIR/closure-no-fn-5.rs:10:27: 10:47]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+ --> $DIR/closure-no-fn-5.rs:10:32
+ |
+LL | let bar: fn() -> u8 = || { a; b; c; d; e };
+ | ^ ^ ^ ^ `d` captured here
+ | | | |
+ | | | `c` captured here
+ | | `b` captured here
+ | `a` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
|
= note: expected fn pointer `for<'r> fn(&'r str)`
found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:50]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+ --> $DIR/closure-reform-bad.rs:10:43
+ |
+LL | let f = |s: &str| println!("{}{}", s, string);
+ | ^^^^^^ `string` captured here
error: aborting due to previous error
|
= note: expected fn pointer `fn(u8) -> u8`
found closure `[main::{closure#0} closure_substs=(unavailable)]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+ --> $DIR/closure-print-verbose.rs:10:39
+ |
+LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+ | ^ `a` captured here
error: aborting due to previous error
LL | println!("{:?}", some_vec);
| ^^^^^^^^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `some_vec`
+note: this function takes ownership of the receiver `self`, which moves `some_vec`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
// check-pass
-// compile-flags: --crate-type lib
+// need to emit MIR, because const prop (which emits `unconditional_panic`) only runs if
+// the `optimized_mir` query is run, which it isn't in check-only mode.
+// compile-flags: --crate-type lib --emit=mir,link
#![warn(unconditional_panic)]
warning: this operation will panic at runtime
- --> $DIR/ice-assert-fail-div-by-zero.rs:11:5
+ --> $DIR/ice-assert-fail-div-by-zero.rs:13:5
|
LL | f.0 / 0;
| ^^^^^^^ attempt to divide `_` by zero
|
note: the lint level is defined here
- --> $DIR/ice-assert-fail-div-by-zero.rs:5:9
+ --> $DIR/ice-assert-fail-div-by-zero.rs:7:9
|
LL | #![warn(unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
LL | const X: u64 = *wat(42);
| ---------------^^^^^^^^-
| |
- | pointer to alloc2 was dereferenced after this allocation got freed
+ | pointer to alloc1 was dereferenced after this allocation got freed
|
= note: `#[deny(const_err)]` on by default
= 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.
error: any use of this value will cause an error
- --> $DIR/ub-nonnull.rs:18:29
+ --> $DIR/ub-nonnull.rs:18:30
|
LL | / const OUT_OF_BOUNDS_PTR: NonNull<u8> = { unsafe {
LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle
LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL!
LL | | let out_of_bounds_ptr = &ptr[255];
- | | ^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1
+ | | ^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1
LL | | mem::transmute(out_of_bounds_ptr)
LL | | } };
| |____-
// # trait object
// bad trait object
+#[warn(const_err)]
const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) };
-//~^ ERROR it is undefined behavior to use this value
+//~^ WARN any use of this value will cause an error [const_err]
// bad trait object
+#[warn(const_err)]
const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
-//~^ ERROR it is undefined behavior to use this value
+//~^ WARN any use of this value will cause an error [const_err]
// bad trait object
+#[warn(const_err)]
const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) };
-//~^ ERROR it is undefined behavior to use this value
+//~^ WARN any use of this value will cause an error [const_err]
const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
//~^ ERROR it is undefined behavior to use this value
const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
//~^ ERROR it is undefined behavior to use this value
const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
//~^ ERROR it is undefined behavior to use this value
+#[warn(const_err)]
const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) };
-//~^ ERROR it is undefined behavior to use this value
+//~^ WARN any use of this value will cause an error [const_err]
// bad data *inside* the trait object
const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
|
= 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.
-error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:98:1
+warning: any use of this value will cause an error
+ --> $DIR/ub-wide-ptr.rs:99:55
|
LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
+ | ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
+ | |
+ | memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N
|
- = 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 lint level is defined here
+ --> $DIR/ub-wide-ptr.rs:98:8
+ |
+LL | #[warn(const_err)]
+ | ^^^^^^^^^
-error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:101:1
+warning: any use of this value will cause an error
+ --> $DIR/ub-wide-ptr.rs:103:55
|
LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
+ | ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
+ | |
+ | memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N
|
- = 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 lint level is defined here
+ --> $DIR/ub-wide-ptr.rs:102:8
+ |
+LL | #[warn(const_err)]
+ | ^^^^^^^^^
-error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:104:1
+warning: any use of this value will cause an error
+ --> $DIR/ub-wide-ptr.rs:107:51
|
LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer
+ | --------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
+ | |
+ | unable to turn bytes into a pointer
|
- = 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 lint level is defined here
+ --> $DIR/ub-wide-ptr.rs:106:8
+ |
+LL | #[warn(const_err)]
+ | ^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:106:1
+ --> $DIR/ub-wide-ptr.rs:109:1
|
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer
= 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.
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:108:1
+ --> $DIR/ub-wide-ptr.rs:111:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function)
= 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.
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:110:1
+ --> $DIR/ub-wide-ptr.rs:113:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function)
|
= 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.
-error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:112:1
+warning: any use of this value will cause an error
+ --> $DIR/ub-wide-ptr.rs:116:63
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function)
+ | --------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
+ | |
+ | "pointer-to-integer cast" needs an rfc before being allowed inside constants
|
- = 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 lint level is defined here
+ --> $DIR/ub-wide-ptr.rs:115:8
+ |
+LL | #[warn(const_err)]
+ | ^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:116:1
+ --> $DIR/ub-wide-ptr.rs:120:1
|
LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.<dyn-downcast>, but expected a boolean
= 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.
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:120:1
+ --> $DIR/ub-wide-ptr.rs:124:1
|
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer
= 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.
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-wide-ptr.rs:122:1
+ --> $DIR/ub-wide-ptr.rs:126:1
|
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
= 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.
error[E0080]: could not evaluate static initializer
- --> $DIR/ub-wide-ptr.rs:128:5
+ --> $DIR/ub-wide-ptr.rs:132:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer
error[E0080]: could not evaluate static initializer
- --> $DIR/ub-wide-ptr.rs:132:5
+ --> $DIR/ub-wide-ptr.rs:136:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N
-error: aborting due to 28 previous errors
+error: aborting due to 24 previous errors; 4 warnings emitted
For more information about this error, try `rustc --explain E0080`.
| ^^^^^^
warning: any use of this value will cause an error
- --> $DIR/const_refers_to_static_cross_crate.rs:26:14
+ --> $DIR/const_refers_to_static_cross_crate.rs:26:15
|
LL | / const U8_MUT2: &u8 = {
LL | | unsafe { &(*static_cross_crate::ZERO_REF)[0] }
- | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
+ | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
LL | |
LL | |
LL | | };
//~^ NOTE
unsafe { std::ptr::raw_const!((*(FOO as *const usize as *const [u8; 1000]))[999]) };
//~^ ERROR any use of this value will cause an error
+//~| NOTE
const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
//~^ ERROR any use of this value will cause an error
= note: `#[deny(const_err)]` on by default
error: any use of this value will cause an error
- --> $DIR/ptr_comparisons.rs:67:14
+ --> $DIR/ptr_comparisons.rs:67:35
|
LL | / const _: *const u8 =
LL | |
LL | | unsafe { std::ptr::raw_const!((*(FOO as *const usize as *const [u8; 1000]))[999]) };
- | |______________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^__-
- | |
- | memory access failed: pointer must be in-bounds at offset 1000, but is outside bounds of alloc2 which has size $WORD
- |
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+ | |___________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^___-
+ | |
+ | memory access failed: pointer must be in-bounds at offset 1000, but is outside bounds of alloc2 which has size $WORD
error: any use of this value will cause an error
- --> $DIR/ptr_comparisons.rs:70:27
+ --> $DIR/ptr_comparisons.rs:71:27
|
LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
| --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
| "pointer-to-integer cast" needs an rfc before being allowed inside constants
error: any use of this value will cause an error
- --> $DIR/ptr_comparisons.rs:75:27
+ --> $DIR/ptr_comparisons.rs:76:27
|
LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 };
| --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
--- /dev/null
+pub struct ZeroLengthThingWithDestructor;
+impl Drop for ZeroLengthThingWithDestructor {
+ fn drop(&mut self) {}
+}
+impl ZeroLengthThingWithDestructor {
+ pub fn new() -> ZeroLengthThingWithDestructor {
+ ZeroLengthThingWithDestructor
+ }
+}
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+// aux-build:issue-10028.rs
+
+// pretty-expanded FIXME #23616
+
+extern crate issue_10028 as issue10028;
+
+use issue10028::ZeroLengthThingWithDestructor;
+
+struct Foo {
+ zero_length_thing: ZeroLengthThingWithDestructor
+}
+
+fn make_foo() -> Foo {
+ Foo { zero_length_thing: ZeroLengthThingWithDestructor::new() }
+}
+
+fn main() {
+ let _f:Foo = make_foo();
+}
// Tests that we can compare various kinds of extern fn signatures.
#![allow(non_camel_case_types)]
-extern fn voidret1() {}
-extern fn voidret2() {}
+// `dbg!()` differentiates these functions to ensure they won't be merged.
+extern fn voidret1() { dbg!() }
+extern fn voidret2() { dbg!() }
extern fn uintret() -> usize { 22 }
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+// pretty-expanded FIXME #23616
+
+unsafe extern fn foo() {}
+unsafe extern "C" fn bar() {}
+
+fn main() {
+ let _a: unsafe extern fn() = foo;
+ let _a: unsafe extern "C" fn() = foo;
+}
--> $DIR/resume-arg-late-bound.rs:15:5
|
LL | test(gen);
- | ^^^^ one type is more general than the other
+ | ^^^^ lifetime mismatch
|
= note: expected type `for<'a> Generator<&'a mut bool>`
found type `Generator<&mut bool>`
+note: the required lifetime does not necessarily outlive the anonymous lifetime #1 defined on the body at 11:15
+ --> $DIR/resume-arg-late-bound.rs:11:15
+ |
+LL | let gen = |arg: &mut bool| {
+ | _______________^
+LL | | yield ();
+LL | | *arg = true;
+LL | | };
+ | |_____^
+note: the lifetime requirement is introduced here
+ --> $DIR/resume-arg-late-bound.rs:8:17
+ |
+LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/resume-arg-late-bound.rs:15:5
|
LL | test(gen);
- | ^^^^ one type is more general than the other
+ | ^^^^ lifetime mismatch
|
= note: expected type `for<'a> Generator<&'a mut bool>`
found type `Generator<&mut bool>`
+note: the anonymous lifetime #1 defined on the body at 11:15 doesn't meet the lifetime requirements
+ --> $DIR/resume-arg-late-bound.rs:11:15
+ |
+LL | let gen = |arg: &mut bool| {
+ | _______________^
+LL | | yield ();
+LL | | *arg = true;
+LL | | };
+ | |_____^
+note: the lifetime requirement is introduced here
+ --> $DIR/resume-arg-late-bound.rs:8:17
+ |
+LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
- | ^^^^^^^^^^^^^^^^ one type is more general than the other
+ | ^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `Bar<&'a isize>`
found type `Bar<&'b isize>`
+note: the required lifetime does not necessarily outlive the lifetime `'b` as defined on the function body at 39:21
+ --> $DIR/hrtb-perfect-forwarding.rs:39:21
+ |
+LL | fn foo_hrtb_bar_not<'b,T>(mut t: T)
+ | ^^
+note: the lifetime requirement is introduced here
+ --> $DIR/hrtb-perfect-forwarding.rs:40:15
+ |
+LL | where T : for<'a> Foo<&'a isize> + Bar<&'b isize>
+ | ^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
- | ^^^^^^^^^^^^^^^^ one type is more general than the other
+ | ^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `Bar<&'a isize>`
found type `Bar<&'b isize>`
+note: the lifetime `'b` as defined on the function body at 39:21 doesn't meet the lifetime requirements
+ --> $DIR/hrtb-perfect-forwarding.rs:39:21
+ |
+LL | fn foo_hrtb_bar_not<'b,T>(mut t: T)
+ | ^^
+note: the lifetime requirement is introduced here
+ --> $DIR/hrtb-perfect-forwarding.rs:40:15
+ |
+LL | where T : for<'a> Foo<&'a isize> + Bar<&'b isize>
+ | ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
LL | let fut = async {
| --- consider giving `fut` the explicit type `impl Future`, with the type parameters specified
LL | make_unit()?;
- | ^ cannot infer type
+ | ^ cannot infer type of error for `?` operator
+ |
+ = note: `?` implicitly converts the error value into a type implementing `From<std::io::Error>`
error: aborting due to previous error; 1 warning emitted
LL | let fut = async {
| --- consider giving `fut` a type
LL | make_unit()?;
- | ^ cannot infer type
+ | ^ cannot infer type of error for `?` operator
+ |
+ = note: `?` implicitly converts the error value into a type implementing `From<std::io::Error>`
error: aborting due to previous error
--- /dev/null
+fn main() {
+ // Below we call the closure with its own return as the argument, unifying
+ // its inferred input and return types. We want to make sure that the generated
+ // error handles this gracefully, and in particular doesn't generate an extra
+ // note about the `?` operator in the closure body, which isn't relevant to
+ // the inference.
+ let x = |r| {
+ //~^ ERROR type annotations needed
+ let v = r?;
+ Ok(v)
+ };
+
+ let _ = x(x(Ok(())));
+}
--- /dev/null
+error[E0282]: type annotations needed for `std::result::Result<(), E>`
+ --> $DIR/cannot-infer-closure-circular.rs:7:14
+ |
+LL | let x = |r| {
+ | ^ consider giving this closure parameter the explicit type `std::result::Result<(), E>`, with the type parameters specified
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
--> $DIR/cannot-infer-closure.rs:3:15
|
LL | Err(a)?;
- | ^ cannot infer type
+ | ^ cannot infer type of error for `?` operator
|
+ = note: `?` implicitly converts the error value into a type implementing `From<()>`
help: give this closure an explicit return type without `_` placeholders
|
LL | let x = |a: (), b: ()| -> std::result::Result<(), _> {
--- /dev/null
+struct QualifiedError<E>(E);
+
+impl<E, T> From<E> for QualifiedError<T>
+where
+ E: std::error::Error,
+ T: From<E>,
+{
+ fn from(e: E) -> QualifiedError<T> {
+ QualifiedError(e.into())
+ }
+}
+
+fn infallible() -> Result<(), std::convert::Infallible> {
+ Ok(())
+}
+
+fn main() {
+ let x = || -> Result<_, QualifiedError<_>> {
+ infallible()?; //~ERROR type annotations needed
+ Ok(())
+ };
+}
--- /dev/null
+error[E0282]: type annotations needed for the closure `fn() -> std::result::Result<(), QualifiedError<_>>`
+ --> $DIR/cannot-infer-partial-try-return.rs:19:9
+ |
+LL | infallible()?;
+ | ^^^^^^^^^^^^^ cannot infer type of error for `?` operator
+ |
+ = note: `?` implicitly converts the error value into `QualifiedError<_>` using its implementation of `From<Infallible>`
+help: give this closure an explicit return type without `_` placeholders
+ |
+LL | let x = || -> std::result::Result<(), QualifiedError<_>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
+++ /dev/null
-pub struct ZeroLengthThingWithDestructor;
-impl Drop for ZeroLengthThingWithDestructor {
- fn drop(&mut self) {}
-}
-impl ZeroLengthThingWithDestructor {
- pub fn new() -> ZeroLengthThingWithDestructor {
- ZeroLengthThingWithDestructor
- }
-}
+++ /dev/null
-pub struct Wrap<A>(pub A);
+++ /dev/null
-// run-pass
-#![allow(dead_code)]
-// pretty-expanded FIXME #23616
-
-unsafe extern fn foo() {}
-unsafe extern "C" fn bar() {}
-
-fn main() {
- let _a: unsafe extern fn() = foo;
- let _a: unsafe extern "C" fn() = foo;
-}
+++ /dev/null
-// run-pass
-#![allow(dead_code)]
-// aux-build:issue-10028.rs
-
-// pretty-expanded FIXME #23616
-
-extern crate issue_10028 as issue10028;
-
-use issue10028::ZeroLengthThingWithDestructor;
-
-struct Foo {
- zero_length_thing: ZeroLengthThingWithDestructor
-}
-
-fn make_foo() -> Foo {
- Foo { zero_length_thing: ZeroLengthThingWithDestructor::new() }
-}
-
-fn main() {
- let _f:Foo = make_foo();
-}
+++ /dev/null
-// run-pass
-// aux-build:issue-10031-aux.rs
-// pretty-expanded FIXME #23616
-
-extern crate issue_10031_aux;
-
-pub fn main() {
- let _ = issue_10031_aux::Wrap(());
-}
+++ /dev/null
-fn f() -> isize {
- (return 1, return 2)
-//~^ ERROR mismatched types
-//~| expected type `isize`
-//~| found tuple `(!, !)`
-//~| expected `isize`, found tuple
-}
-
-fn main() {}
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/issue-10176.rs:2:5
- |
-LL | fn f() -> isize {
- | ----- expected `isize` because of return type
-LL | (return 1, return 2)
- | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found tuple
- |
- = note: expected type `isize`
- found tuple `(!, !)`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-struct Foo(bool);
-fn foo(_: usize) -> Foo { Foo(false) }
-
-fn main() {
- match Foo(true) {
- foo(x) //~ ERROR expected tuple struct or tuple variant, found function `foo`
- => ()
- }
-}
+++ /dev/null
-error[E0532]: expected tuple struct or tuple variant, found function `foo`
- --> $DIR/issue-10200.rs:6:9
- |
-LL | struct Foo(bool);
- | ----------------- similarly named tuple struct `Foo` defined here
-...
-LL | foo(x)
- | ^^^ help: a tuple struct with a similar name exists (notice the capitalization): `Foo`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0532`.
| ------------ type in trait
...
LL | fn bar(&mut self, other: &dyn Foo) {}
- | ^^^^^^^^ types differ in mutability
+ | ^^^^^^^^
+ | |
+ | types differ in mutability
+ | help: consider changing the mutability to match the trait: `&mut dyn Foo`
|
= note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)`
found fn pointer `fn(&mut Baz, &dyn Foo)`
-help: consider change the type to match the mutability in trait
- |
-LL | fn bar(&mut self, other: &mut dyn Foo) {}
- | ^^^^^^^^^^^^
error: aborting due to previous error
|
LL | foo::<&'a i32>();
| ^^^^^^^^^^^^^^
- |
- = note: type must outlive any other region
error: aborting due to previous error
LL | x.zero()
| ^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
+note: this function takes ownership of the receiver `self`, which moves `x`
--> $DIR/issue-34721.rs:4:13
|
LL | fn zero(self) -> Self;
|
= note: expected type `FnOnce<(&'a bool,)>`
found type `FnOnce<(&bool,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57843.rs:23:18
+ |
+LL | Foo(Box::new(|_| ()));
+ | ^^^^^^
error: aborting due to previous error
LL | bad_letters.push('s');
| ^^^^^^^^^^^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `bad_letters`
+note: this function takes ownership of the receiver `self`, which moves `bad_letters`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| |
| value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `orig`
+note: this function takes ownership of the receiver `self`, which moves `orig`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-79187-2.rs:9:24
+ |
+LL | take_foo(|a: &i32| a);
+ | - - ^ returning this value requires that `'1` must outlive `'2`
+ | | |
+ | | return type of closure is &'2 i32
+ | let's call the lifetime of this reference `'1`
+
+error: lifetime may not live long enough
+ --> $DIR/issue-79187-2.rs:10:34
+ |
+LL | take_foo(|a: &i32| -> &i32 { a });
+ | - - ^ returning this value requires that `'1` must outlive `'2`
+ | | |
+ | | let's call the lifetime of this reference `'2`
+ | let's call the lifetime of this reference `'1`
+
+error: higher-ranked subtype error
+ --> $DIR/issue-79187-2.rs:8:5
+ |
+LL | take_foo(|a| a);
+ | ^^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-79187-2.rs:8:5
+ |
+LL | take_foo(|a| a);
+ | ^^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-79187-2.rs:9:5
+ |
+LL | take_foo(|a: &i32| a);
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-79187-2.rs:10:5
+ |
+LL | take_foo(|a: &i32| -> &i32 { a });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+trait Foo {}
+
+impl<F> Foo for F where F: Fn(&i32) -> &i32 {}
+
+fn take_foo(_: impl Foo) {}
+
+fn main() {
+ take_foo(|a| a); //~ ERROR mismatched types
+ take_foo(|a: &i32| a); //~ ERROR mismatched types
+ take_foo(|a: &i32| -> &i32 { a }); //~ ERROR mismatched types
+
+ // OK
+ take_foo(identity(|a| a));
+ take_foo(identity(|a: &i32| a));
+ take_foo(identity(|a: &i32| -> &i32 { a }));
+
+ fn identity<F>(t: F) -> F
+ where
+ F: Fn(&i32) -> &i32,
+ {
+ t
+ }
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-79187-2.rs:8:5
+ |
+LL | take_foo(|a| a);
+ | ^^^^^^^^ lifetime mismatch
+ |
+ = note: expected type `for<'r> Fn<(&'r i32,)>`
+ found type `Fn<(&i32,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-79187-2.rs:8:14
+ |
+LL | take_foo(|a| a);
+ | ^^^^^
+note: the lifetime requirement is introduced here
+ --> $DIR/issue-79187-2.rs:5:21
+ |
+LL | fn take_foo(_: impl Foo) {}
+ | ^^^
+
+error[E0308]: mismatched types
+ --> $DIR/issue-79187-2.rs:9:5
+ |
+LL | take_foo(|a: &i32| a);
+ | ^^^^^^^^ lifetime mismatch
+ |
+ = note: expected reference `&i32`
+ found reference `&i32`
+note: the anonymous lifetime #1 defined on the body at 9:14 doesn't meet the lifetime requirements
+ --> $DIR/issue-79187-2.rs:9:14
+ |
+LL | take_foo(|a: &i32| a);
+ | ^^^^^^^^^^^
+note: the lifetime requirement is introduced here
+ --> $DIR/issue-79187-2.rs:5:21
+ |
+LL | fn take_foo(_: impl Foo) {}
+ | ^^^
+
+error[E0308]: mismatched types
+ --> $DIR/issue-79187-2.rs:10:5
+ |
+LL | take_foo(|a: &i32| -> &i32 { a });
+ | ^^^^^^^^ lifetime mismatch
+ |
+ = note: expected reference `&i32`
+ found reference `&i32`
+note: the anonymous lifetime #1 defined on the body at 10:14 doesn't meet the lifetime requirements
+ --> $DIR/issue-79187-2.rs:10:14
+ |
+LL | take_foo(|a: &i32| -> &i32 { a });
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+note: the lifetime requirement is introduced here
+ --> $DIR/issue-79187-2.rs:5:21
+ |
+LL | fn take_foo(_: impl Foo) {}
+ | ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error: higher-ranked subtype error
+ --> $DIR/issue-79187.rs:5:5
+ |
+LL | thing(f);
+ | ^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-79187.rs:5:5
+ |
+LL | thing(f);
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+fn thing(x: impl FnOnce(&u32)) {}
+
+fn main() {
+ let f = |_| ();
+ thing(f); //~ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-79187.rs:5:5
+ |
+LL | thing(f);
+ | ^^^^^ lifetime mismatch
+ |
+ = note: expected type `FnOnce<(&u32,)>`
+ found type `FnOnce<(&u32,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-79187.rs:4:13
+ |
+LL | let f = |_| ();
+ | ^^^^^^
+note: the lifetime requirement is introduced here
+ --> $DIR/issue-79187.rs:1:18
+ |
+LL | fn thing(x: impl FnOnce(&u32)) {}
+ | ^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// check-pass
+#![crate_type = "lib"]
+
+#[cfg(target_arch = "wasm32")]
+mod wasm_non_clash {
+ mod a {
+ #[link(wasm_import_module = "a")]
+ extern "C" {
+ pub fn foo();
+ }
+ }
+
+ mod b {
+ #[link(wasm_import_module = "b")]
+ extern "C" {
+ pub fn foo() -> usize;
+ // #79581: These declarations shouldn't clash because foreign fn names are mangled
+ // on wasm32.
+ }
+ }
+}
| ----- type in trait
...
LL | fn bar(&mut self) { }
- | ^^^^^^^^^ types differ in mutability
+ | ^^^^^^^^^
+ | |
+ | types differ in mutability
+ | help: consider changing the mutability to match the trait: `&self`
|
= note: expected fn pointer `fn(&Bar)`
found fn pointer `fn(&mut Bar)`
-help: consider change the type to match the mutability in trait
- |
-LL | fn bar(&self) { }
- | ^^^^^
error: aborting due to 2 previous errors
--> $DIR/closure-arg-type-mismatch.rs:10:5
|
LL | baz(f);
- | ^^^ one type is more general than the other
+ | ^^^ lifetime mismatch
|
= note: expected type `for<'r> Fn<(*mut &'r u32,)>`
found type `Fn<(*mut &'a u32,)>`
+note: the required lifetime does not necessarily outlive the lifetime `'a` as defined on the function body at 9:10
+ --> $DIR/closure-arg-type-mismatch.rs:9:10
+ |
+LL | fn _test<'a>(f: fn(*mut &'a u32)) {
+ | ^^
+note: the lifetime requirement is introduced here
+ --> $DIR/closure-arg-type-mismatch.rs:8:11
+ |
+LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
+ | ^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/closure-arg-type-mismatch.rs:10:5
|
LL | baz(f);
- | ^^^ one type is more general than the other
+ | ^^^ lifetime mismatch
|
= note: expected type `FnOnce<(*mut &u32,)>`
found type `FnOnce<(*mut &'a u32,)>`
+note: the required lifetime does not necessarily outlive the lifetime `'a` as defined on the function body at 9:10
+ --> $DIR/closure-arg-type-mismatch.rs:9:10
+ |
+LL | fn _test<'a>(f: fn(*mut &'a u32)) {
+ | ^^
+note: the lifetime requirement is introduced here
+ --> $DIR/closure-arg-type-mismatch.rs:8:11
+ |
+LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
+ | ^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/closure-arg-type-mismatch.rs:10:5
|
LL | baz(f);
- | ^^^ one type is more general than the other
+ | ^^^ lifetime mismatch
|
= note: expected type `for<'r> Fn<(*mut &'r u32,)>`
found type `Fn<(*mut &'a u32,)>`
+note: the lifetime `'a` as defined on the function body at 9:10 doesn't meet the lifetime requirements
+ --> $DIR/closure-arg-type-mismatch.rs:9:10
+ |
+LL | fn _test<'a>(f: fn(*mut &'a u32)) {
+ | ^^
+note: the lifetime requirement is introduced here
+ --> $DIR/closure-arg-type-mismatch.rs:8:11
+ |
+LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
+ | ^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/closure-arg-type-mismatch.rs:10:5
|
LL | baz(f);
- | ^^^ one type is more general than the other
+ | ^^^ lifetime mismatch
|
= note: expected type `FnOnce<(*mut &u32,)>`
found type `FnOnce<(*mut &'a u32,)>`
+note: the lifetime `'a` as defined on the function body at 9:10 doesn't meet the lifetime requirements
+ --> $DIR/closure-arg-type-mismatch.rs:9:10
+ |
+LL | fn _test<'a>(f: fn(*mut &'a u32)) {
+ | ^^
+note: the lifetime requirement is introduced here
+ --> $DIR/closure-arg-type-mismatch.rs:8:11
+ |
+LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
+ | ^^^^^^^^^^^^^
error: aborting due to 7 previous errors
--> $DIR/closure-mismatch.rs:8:5
|
LL | baz(|_| ());
- | ^^^ one type is more general than the other
+ | ^^^ lifetime mismatch
|
= note: expected type `for<'r> Fn<(&'r (),)>`
found type `Fn<(&(),)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/closure-mismatch.rs:8:9
+ |
+LL | baz(|_| ());
+ | ^^^^^^
+note: the lifetime requirement is introduced here
+ --> $DIR/closure-mismatch.rs:5:11
+ |
+LL | fn baz<T: Foo>(_: T) {}
+ | ^^^
error: aborting due to previous error
| -------- type in trait
...
LL | fn bar(&mut self, bar: &Bar) { }
- | ^^^^ types differ in mutability
+ | ^^^^
+ | |
+ | types differ in mutability
+ | help: consider changing the mutability to match the trait: `&mut Bar`
|
= note: expected fn pointer `fn(&mut Bar, &mut Bar)`
found fn pointer `fn(&mut Bar, &Bar)`
-help: consider change the type to match the mutability in trait
- |
-LL | fn bar(&mut self, bar: &mut Bar) { }
- | ^^^^^^^^
error: aborting due to 2 previous errors
let container = Container(vec![]);
for _val in container.custom_into_iter() {}
container; //~ ERROR use of moved
+
+ let foo2 = Foo;
+ loop {
+ foo2.use_self(); //~ ERROR use of moved
+ }
}
fn main() {}
LL | val.0;
| ^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `val.0`
+note: this function takes ownership of the receiver `self`, which moves `val.0`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
LL | foo;
| ^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `foo`
+note: this function takes ownership of the receiver `self`, which moves `foo`
--> $DIR/move-fn-self-receiver.rs:13:17
|
LL | fn use_self(self) {}
LL | boxed_foo;
| ^^^^^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `boxed_foo`
+note: this function takes ownership of the receiver `self`, which moves `boxed_foo`
--> $DIR/move-fn-self-receiver.rs:14:21
|
LL | fn use_box_self(self: Box<Self>) {}
LL | pin_box_foo;
| ^^^^^^^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `pin_box_foo`
+note: this function takes ownership of the receiver `self`, which moves `pin_box_foo`
--> $DIR/move-fn-self-receiver.rs:15:25
|
LL | fn use_pin_box_self(self: Pin<Box<Self>>) {}
LL | rc_foo;
| ^^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `rc_foo`
+note: this function takes ownership of the receiver `self`, which moves `rc_foo`
--> $DIR/move-fn-self-receiver.rs:16:20
|
LL | fn use_rc_self(self: Rc<Self>) {}
LL | container;
| ^^^^^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `container`
+note: this function takes ownership of the receiver `self`, which moves `container`
--> $DIR/move-fn-self-receiver.rs:23:25
|
LL | fn custom_into_iter(self) -> impl Iterator<Item = bool> {
| ^^^^
-error: aborting due to 11 previous errors
+error[E0382]: use of moved value: `foo2`
+ --> $DIR/move-fn-self-receiver.rs:75:9
+ |
+LL | let foo2 = Foo;
+ | ---- move occurs because `foo2` has type `Foo`, which does not implement the `Copy` trait
+LL | loop {
+LL | foo2.use_self();
+ | ^^^^ ---------- `foo2` moved due to this method call, in previous iteration of loop
+
+error: aborting due to 12 previous errors
Some errors have detailed explanations: E0382, E0505.
For more information about an error, try `rustc --explain E0382`.
LL | touch(&x[0]);
| ^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
+note: this function takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
LL | touch(&x);
| ^^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
+note: this function takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
LL | touch(&x);
| ^^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
+note: this function takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
--- /dev/null
+fn f() -> isize {
+ (return 1, return 2)
+//~^ ERROR mismatched types
+//~| expected type `isize`
+//~| found tuple `(!, !)`
+//~| expected `isize`, found tuple
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-10176.rs:2:5
+ |
+LL | fn f() -> isize {
+ | ----- expected `isize` because of return type
+LL | (return 1, return 2)
+ | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found tuple
+ |
+ = note: expected type `isize`
+ found tuple `(!, !)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
}
fn f5() {
- async //~ ERROR async closures are unstable
+ async
let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let`
}
LL | let x = 0;
| ^^^ unexpected token
-error[E0658]: async closures are unstable
- --> $DIR/block-no-opening-brace.rs:29:5
- |
-LL | async
- | ^^^^^
- |
- = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
- = help: add `#![feature(async_closure)]` to the crate attributes to enable
-
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
-For more information about this error, try `rustc --explain E0658`.
#![crate_type = "rlib"]
pub enum EmptyForeignEnum {}
+
+pub struct VisiblyUninhabitedForeignStruct {
+ pub field: EmptyForeignEnum,
+}
+
+pub struct SecretlyUninhabitedForeignStruct {
+ _priv: EmptyForeignEnum,
+}
--- /dev/null
+error: unreachable pattern
+ --> $DIR/empty-match.rs:37:9
+ |
+LL | _ => {},
+ | ^
+ |
+note: the lint level is defined here
+ --> $DIR/empty-match.rs:8:9
+ |
+LL | #![deny(unreachable_patterns)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:40:9
+ |
+LL | _ if false => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:47:9
+ |
+LL | _ => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:50:9
+ |
+LL | _ if false => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:57:9
+ |
+LL | _ => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:60:9
+ |
+LL | _ if false => {},
+ | ^
+
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+ --> $DIR/empty-match.rs:78:20
+ |
+LL | match_no_arms!(0u8);
+ | ^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `u8`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
+ --> $DIR/empty-match.rs:79:20
+ |
+LL | struct NonEmptyStruct1;
+ | ----------------------- `NonEmptyStruct1` defined here
+...
+LL | match_no_arms!(NonEmptyStruct1);
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct1`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
+ --> $DIR/empty-match.rs:80:20
+ |
+LL | struct NonEmptyStruct2(bool);
+ | ----------------------------- `NonEmptyStruct2` defined here
+...
+LL | match_no_arms!(NonEmptyStruct2(true));
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct2`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
+ --> $DIR/empty-match.rs:81:20
+ |
+LL | / union NonEmptyUnion1 {
+LL | | foo: (),
+LL | | }
+ | |_- `NonEmptyUnion1` defined here
+...
+LL | match_no_arms!((NonEmptyUnion1 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion1`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
+ --> $DIR/empty-match.rs:82:20
+ |
+LL | / union NonEmptyUnion2 {
+LL | | foo: (),
+LL | | bar: (),
+LL | | }
+ | |_- `NonEmptyUnion2` defined here
+...
+LL | match_no_arms!((NonEmptyUnion2 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion2`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
+ --> $DIR/empty-match.rs:83:20
+ |
+LL | / enum NonEmptyEnum1 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum1` defined here
+...
+LL | match_no_arms!(NonEmptyEnum1::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum1`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
+ --> $DIR/empty-match.rs:84:20
+ |
+LL | / enum NonEmptyEnum2 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | Bar,
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum2` defined here
+...
+LL | match_no_arms!(NonEmptyEnum2::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum2`
+
+error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
+ --> $DIR/empty-match.rs:85:20
+ |
+LL | / enum NonEmptyEnum5 {
+LL | | V1, V2, V3, V4, V5,
+LL | | }
+ | |_- `NonEmptyEnum5` defined here
+...
+LL | match_no_arms!(NonEmptyEnum5::V1);
+ | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum5`
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/empty-match.rs:87:24
+ |
+LL | match_guarded_arm!(0u8);
+ | ^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `u8`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
+ --> $DIR/empty-match.rs:88:24
+ |
+LL | struct NonEmptyStruct1;
+ | ----------------------- `NonEmptyStruct1` defined here
+...
+LL | match_guarded_arm!(NonEmptyStruct1);
+ | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct1`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
+ --> $DIR/empty-match.rs:89:24
+ |
+LL | struct NonEmptyStruct2(bool);
+ | ----------------------------- `NonEmptyStruct2` defined here
+...
+LL | match_guarded_arm!(NonEmptyStruct2(true));
+ | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct2`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
+ --> $DIR/empty-match.rs:90:24
+ |
+LL | / union NonEmptyUnion1 {
+LL | | foo: (),
+LL | | }
+ | |_- `NonEmptyUnion1` defined here
+...
+LL | match_guarded_arm!((NonEmptyUnion1 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion1`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
+ --> $DIR/empty-match.rs:91:24
+ |
+LL | / union NonEmptyUnion2 {
+LL | | foo: (),
+LL | | bar: (),
+LL | | }
+ | |_- `NonEmptyUnion2` defined here
+...
+LL | match_guarded_arm!((NonEmptyUnion2 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion2`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
+ --> $DIR/empty-match.rs:92:24
+ |
+LL | / enum NonEmptyEnum1 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum1` defined here
+...
+LL | match_guarded_arm!(NonEmptyEnum1::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum1`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
+ --> $DIR/empty-match.rs:93:24
+ |
+LL | / enum NonEmptyEnum2 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | Bar,
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum2` defined here
+...
+LL | match_guarded_arm!(NonEmptyEnum2::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum2`
+
+error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
+ --> $DIR/empty-match.rs:94:24
+ |
+LL | / enum NonEmptyEnum5 {
+LL | | V1, V2, V3, V4, V5,
+LL | | }
+ | |_- `NonEmptyEnum5` defined here
+...
+LL | match_guarded_arm!(NonEmptyEnum5::V1);
+ | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum5`
+
+error: aborting due to 22 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+error: unreachable pattern
+ --> $DIR/empty-match.rs:37:9
+ |
+LL | _ => {},
+ | ^
+ |
+note: the lint level is defined here
+ --> $DIR/empty-match.rs:8:9
+ |
+LL | #![deny(unreachable_patterns)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:40:9
+ |
+LL | _ if false => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:47:9
+ |
+LL | _ => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:50:9
+ |
+LL | _ if false => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:57:9
+ |
+LL | _ => {},
+ | ^
+
+error: unreachable pattern
+ --> $DIR/empty-match.rs:60:9
+ |
+LL | _ if false => {},
+ | ^
+
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+ --> $DIR/empty-match.rs:78:20
+ |
+LL | match_no_arms!(0u8);
+ | ^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `u8`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
+ --> $DIR/empty-match.rs:79:20
+ |
+LL | struct NonEmptyStruct1;
+ | ----------------------- `NonEmptyStruct1` defined here
+...
+LL | match_no_arms!(NonEmptyStruct1);
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct1`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
+ --> $DIR/empty-match.rs:80:20
+ |
+LL | struct NonEmptyStruct2(bool);
+ | ----------------------------- `NonEmptyStruct2` defined here
+...
+LL | match_no_arms!(NonEmptyStruct2(true));
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct2`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
+ --> $DIR/empty-match.rs:81:20
+ |
+LL | / union NonEmptyUnion1 {
+LL | | foo: (),
+LL | | }
+ | |_- `NonEmptyUnion1` defined here
+...
+LL | match_no_arms!((NonEmptyUnion1 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion1`
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
+ --> $DIR/empty-match.rs:82:20
+ |
+LL | / union NonEmptyUnion2 {
+LL | | foo: (),
+LL | | bar: (),
+LL | | }
+ | |_- `NonEmptyUnion2` defined here
+...
+LL | match_no_arms!((NonEmptyUnion2 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion2`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
+ --> $DIR/empty-match.rs:83:20
+ |
+LL | / enum NonEmptyEnum1 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum1` defined here
+...
+LL | match_no_arms!(NonEmptyEnum1::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum1`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
+ --> $DIR/empty-match.rs:84:20
+ |
+LL | / enum NonEmptyEnum2 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | Bar,
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum2` defined here
+...
+LL | match_no_arms!(NonEmptyEnum2::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum2`
+
+error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
+ --> $DIR/empty-match.rs:85:20
+ |
+LL | / enum NonEmptyEnum5 {
+LL | | V1, V2, V3, V4, V5,
+LL | | }
+ | |_- `NonEmptyEnum5` defined here
+...
+LL | match_no_arms!(NonEmptyEnum5::V1);
+ | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum5`
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/empty-match.rs:87:24
+ |
+LL | match_guarded_arm!(0u8);
+ | ^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `u8`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
+ --> $DIR/empty-match.rs:88:24
+ |
+LL | struct NonEmptyStruct1;
+ | ----------------------- `NonEmptyStruct1` defined here
+...
+LL | match_guarded_arm!(NonEmptyStruct1);
+ | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct1`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
+ --> $DIR/empty-match.rs:89:24
+ |
+LL | struct NonEmptyStruct2(bool);
+ | ----------------------------- `NonEmptyStruct2` defined here
+...
+LL | match_guarded_arm!(NonEmptyStruct2(true));
+ | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyStruct2`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
+ --> $DIR/empty-match.rs:90:24
+ |
+LL | / union NonEmptyUnion1 {
+LL | | foo: (),
+LL | | }
+ | |_- `NonEmptyUnion1` defined here
+...
+LL | match_guarded_arm!((NonEmptyUnion1 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion1`
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
+ --> $DIR/empty-match.rs:91:24
+ |
+LL | / union NonEmptyUnion2 {
+LL | | foo: (),
+LL | | bar: (),
+LL | | }
+ | |_- `NonEmptyUnion2` defined here
+...
+LL | match_guarded_arm!((NonEmptyUnion2 { foo: () }));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyUnion2`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
+ --> $DIR/empty-match.rs:92:24
+ |
+LL | / enum NonEmptyEnum1 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum1` defined here
+...
+LL | match_guarded_arm!(NonEmptyEnum1::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum1`
+
+error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
+ --> $DIR/empty-match.rs:93:24
+ |
+LL | / enum NonEmptyEnum2 {
+LL | | Foo(bool),
+ | | --- not covered
+LL | | Bar,
+ | | --- not covered
+LL | | }
+ | |_- `NonEmptyEnum2` defined here
+...
+LL | match_guarded_arm!(NonEmptyEnum2::Foo(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum2`
+
+error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
+ --> $DIR/empty-match.rs:94:24
+ |
+LL | / enum NonEmptyEnum5 {
+LL | | V1, V2, V3, V4, V5,
+LL | | }
+ | |_- `NonEmptyEnum5` defined here
+...
+LL | match_guarded_arm!(NonEmptyEnum5::V1);
+ | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `NonEmptyEnum5`
+
+error: aborting due to 22 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+// aux-build:empty.rs
+// revisions: normal exhaustive_patterns
+//
+// This tests a match with no arms on various types.
+#![feature(never_type)]
+#![feature(never_type_fallback)]
+#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
+#![deny(unreachable_patterns)]
+
+extern crate empty;
+
+enum EmptyEnum {}
+
+struct NonEmptyStruct1;
+struct NonEmptyStruct2(bool);
+union NonEmptyUnion1 {
+ foo: (),
+}
+union NonEmptyUnion2 {
+ foo: (),
+ bar: (),
+}
+enum NonEmptyEnum1 {
+ Foo(bool),
+}
+enum NonEmptyEnum2 {
+ Foo(bool),
+ Bar,
+}
+enum NonEmptyEnum5 {
+ V1, V2, V3, V4, V5,
+}
+
+fn empty_enum(x: EmptyEnum) {
+ match x {} // ok
+ match x {
+ _ => {}, //~ ERROR unreachable pattern
+ }
+ match x {
+ _ if false => {}, //~ ERROR unreachable pattern
+ }
+}
+
+fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
+ match x {} // ok
+ match x {
+ _ => {}, //~ ERROR unreachable pattern
+ }
+ match x {
+ _ if false => {}, //~ ERROR unreachable pattern
+ }
+}
+
+fn never(x: !) {
+ match x {} // ok
+ match x {
+ _ => {}, //~ ERROR unreachable pattern
+ }
+ match x {
+ _ if false => {}, //~ ERROR unreachable pattern
+ }
+}
+
+macro_rules! match_no_arms {
+ ($e:expr) => {
+ match $e {}
+ };
+}
+macro_rules! match_guarded_arm {
+ ($e:expr) => {
+ match $e {
+ _ if false => {}
+ }
+ };
+}
+
+fn main() {
+ match_no_arms!(0u8); //~ ERROR type `u8` is non-empty
+ match_no_arms!(NonEmptyStruct1); //~ ERROR type `NonEmptyStruct1` is non-empty
+ match_no_arms!(NonEmptyStruct2(true)); //~ ERROR type `NonEmptyStruct2` is non-empty
+ match_no_arms!((NonEmptyUnion1 { foo: () })); //~ ERROR type `NonEmptyUnion1` is non-empty
+ match_no_arms!((NonEmptyUnion2 { foo: () })); //~ ERROR type `NonEmptyUnion2` is non-empty
+ match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `Foo(_)` not covered
+ match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `Foo(_)` and `Bar` not covered
+ match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `V1`, `V2`, `V3` and 2 more not covered
+
+ match_guarded_arm!(0u8); //~ ERROR `_` not covered
+ match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
+ match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
+ match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
+ match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
+ match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `Foo(_)` not covered
+ match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `Foo(_)` and `Bar` not covered
+ match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `V1`, `V2`, `V3` and 2 more not covered
+}
+++ /dev/null
-#![feature(precise_pointer_size_matching)]
-#![feature(exclusive_range_pattern)]
-
-macro_rules! m {
- ($s:expr, $($t:tt)+) => {
- match $s { $($t)+ => {} }
- }
-}
-
-fn main() {
- match 0usize {
- 0 ..= usize::MAX => {}
- }
-
- match 0isize {
- isize::MIN ..= isize::MAX => {}
- }
-
- m!(0usize, 0..=usize::MAX);
- m!(0usize, 0..5 | 5..=usize::MAX);
- m!(0usize, 0..usize::MAX | usize::MAX);
- m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
-
- m!(0isize, isize::MIN..=isize::MAX);
- m!(0isize, isize::MIN..5 | 5..=isize::MAX);
- m!(0isize, isize::MIN..isize::MAX | isize::MAX);
- m!((0isize, true), (isize::MIN..5, true)
- | (5..=isize::MAX, true) | (isize::MIN..=isize::MAX, false));
-
- match 0isize {
- isize::MIN ..= -1 => {}
- 0 => {}
- 1 ..= isize::MAX => {}
- }
-
- match 7usize {}
- //~^ ERROR non-exhaustive patterns
-}
+++ /dev/null
-error[E0004]: non-exhaustive patterns: type `usize` is non-empty
- --> $DIR/pointer-sized-int-allow.rs:36:11
- |
-LL | match 7usize {}
- | ^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `usize`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0004`.
+++ /dev/null
-#![feature(exclusive_range_pattern)]
-
-macro_rules! m {
- ($s:expr, $($t:tt)+) => {
- match $s { $($t)+ => {} }
- }
-}
-
-fn main() {
- match 0usize {
- //~^ ERROR non-exhaustive patterns
- 0 ..= usize::MAX => {}
- }
-
- match 0isize {
- //~^ ERROR non-exhaustive patterns
- isize::MIN ..= isize::MAX => {}
- }
-
- m!(0usize, 0..=usize::MAX);
- //~^ ERROR non-exhaustive patterns
- m!(0usize, 0..5 | 5..=usize::MAX);
- //~^ ERROR non-exhaustive patterns
- m!(0usize, 0..usize::MAX | usize::MAX);
- //~^ ERROR non-exhaustive patterns
- m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
- //~^ ERROR non-exhaustive patterns
-
- m!(0isize, isize::MIN..=isize::MAX);
- //~^ ERROR non-exhaustive patterns
- m!(0isize, isize::MIN..5 | 5..=isize::MAX);
- //~^ ERROR non-exhaustive patterns
- m!(0isize, isize::MIN..isize::MAX | isize::MAX);
- //~^ ERROR non-exhaustive patterns
- m!((0isize, true), (isize::MIN..5, true)
- | (5..=isize::MAX, true) | (isize::MIN..=isize::MAX, false));
- //~^^ ERROR non-exhaustive patterns
-
- match 0isize {
- //~^ ERROR non-exhaustive patterns
- isize::MIN ..= -1 => {}
- 0 => {}
- 1 ..= isize::MAX => {}
- }
-
- match 7usize {}
- //~^ ERROR non-exhaustive patterns
-}
+++ /dev/null
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:10:11
- |
-LL | match 0usize {
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `usize`
- = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:15:11
- |
-LL | match 0isize {
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `isize`
- = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:20:8
- |
-LL | m!(0usize, 0..=usize::MAX);
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `usize`
- = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:22:8
- |
-LL | m!(0usize, 0..5 | 5..=usize::MAX);
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `usize`
- = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:24:8
- |
-LL | m!(0usize, 0..usize::MAX | usize::MAX);
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `usize`
- = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
-
-error[E0004]: non-exhaustive patterns: `(_, _)` not covered
- --> $DIR/pointer-sized-int-deny.rs:26:8
- |
-LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
- | ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `(usize, bool)`
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:29:8
- |
-LL | m!(0isize, isize::MIN..=isize::MAX);
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `isize`
- = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:31:8
- |
-LL | m!(0isize, isize::MIN..5 | 5..=isize::MAX);
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `isize`
- = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:33:8
- |
-LL | m!(0isize, isize::MIN..isize::MAX | isize::MAX);
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `isize`
- = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
-
-error[E0004]: non-exhaustive patterns: `(_, _)` not covered
- --> $DIR/pointer-sized-int-deny.rs:35:8
- |
-LL | m!((0isize, true), (isize::MIN..5, true)
- | ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `(isize, bool)`
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/pointer-sized-int-deny.rs:39:11
- |
-LL | match 0isize {
- | ^^^^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `isize`
- = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
- = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
-
-error[E0004]: non-exhaustive patterns: type `usize` is non-empty
- --> $DIR/pointer-sized-int-deny.rs:46:11
- |
-LL | match 7usize {}
- | ^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `usize`
-
-error: aborting due to 12 previous errors
-
-For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+error[E0004]: non-exhaustive patterns: type `usize` is non-empty
+ --> $DIR/pointer-sized-int.rs:48:11
+ |
+LL | match 7usize {}
+ | ^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `usize`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:12:11
+ |
+LL | match 0usize {
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `usize`
+ = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:17:11
+ |
+LL | match 0isize {
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `isize`
+ = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:22:8
+ |
+LL | m!(0usize, 0..=usize::MAX);
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `usize`
+ = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:24:8
+ |
+LL | m!(0usize, 0..5 | 5..=usize::MAX);
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `usize`
+ = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:26:8
+ |
+LL | m!(0usize, 0..usize::MAX | usize::MAX);
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `usize`
+ = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+ --> $DIR/pointer-sized-int.rs:28:8
+ |
+LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
+ | ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `(usize, bool)`
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:31:8
+ |
+LL | m!(0isize, isize::MIN..=isize::MAX);
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `isize`
+ = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:33:8
+ |
+LL | m!(0isize, isize::MIN..5 | 5..=isize::MAX);
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `isize`
+ = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:35:8
+ |
+LL | m!(0isize, isize::MIN..isize::MAX | isize::MAX);
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `isize`
+ = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+ --> $DIR/pointer-sized-int.rs:37:8
+ |
+LL | m!((0isize, true), (isize::MIN..5, true)
+ | ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `(isize, bool)`
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/pointer-sized-int.rs:41:11
+ |
+LL | match 0isize {
+ | ^^^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `isize`
+ = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+ = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+
+error[E0004]: non-exhaustive patterns: type `usize` is non-empty
+ --> $DIR/pointer-sized-int.rs:48:11
+ |
+LL | match 7usize {}
+ | ^^^^^^
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+ = note: the matched value is of type `usize`
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+// revisions: allow deny
+#![feature(exclusive_range_pattern)]
+#![cfg_attr(allow, feature(precise_pointer_size_matching))]
+
+macro_rules! m {
+ ($s:expr, $($t:tt)+) => {
+ match $s { $($t)+ => {} }
+ }
+}
+
+fn main() {
+ match 0usize {
+ //[deny]~^ ERROR non-exhaustive patterns
+ 0 ..= usize::MAX => {}
+ }
+
+ match 0isize {
+ //[deny]~^ ERROR non-exhaustive patterns
+ isize::MIN ..= isize::MAX => {}
+ }
+
+ m!(0usize, 0..=usize::MAX);
+ //[deny]~^ ERROR non-exhaustive patterns
+ m!(0usize, 0..5 | 5..=usize::MAX);
+ //[deny]~^ ERROR non-exhaustive patterns
+ m!(0usize, 0..usize::MAX | usize::MAX);
+ //[deny]~^ ERROR non-exhaustive patterns
+ m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
+ //[deny]~^ ERROR non-exhaustive patterns
+
+ m!(0isize, isize::MIN..=isize::MAX);
+ //[deny]~^ ERROR non-exhaustive patterns
+ m!(0isize, isize::MIN..5 | 5..=isize::MAX);
+ //[deny]~^ ERROR non-exhaustive patterns
+ m!(0isize, isize::MIN..isize::MAX | isize::MAX);
+ //[deny]~^ ERROR non-exhaustive patterns
+ m!((0isize, true), (isize::MIN..5, true)
+ | (5..=isize::MAX, true) | (isize::MIN..=isize::MAX, false));
+ //[deny]~^^ ERROR non-exhaustive patterns
+
+ match 0isize {
+ //[deny]~^ ERROR non-exhaustive patterns
+ isize::MIN ..= -1 => {}
+ 0 => {}
+ 1 ..= isize::MAX => {}
+ }
+
+ match 7usize {}
+ //~^ ERROR non-exhaustive patterns
+}
+++ /dev/null
-// aux-build:empty.rs
-#![feature(never_type)]
-#![feature(never_type_fallback)]
-#![feature(exhaustive_patterns)]
-#![deny(unreachable_patterns)]
-
-extern crate empty;
-
-enum EmptyEnum {}
-
-struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
-union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
- foo: (),
-}
-union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here
- foo: (),
- bar: (),
-}
-enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here
- Foo(bool),
- //~^ not covered
- //~| not covered
-}
-enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here
- Foo(bool),
- //~^ not covered
- //~| not covered
- Bar,
- //~^ not covered
- //~| not covered
-}
-enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here
- V1, V2, V3, V4, V5,
-}
-
-macro_rules! match_empty {
- ($e:expr) => {
- match $e {}
- };
-}
-macro_rules! match_false {
- ($e:expr) => {
- match $e {
- _ if false => {}
- }
- };
-}
-
-fn empty_enum(x: EmptyEnum) {
- match x {} // ok
- match x {
- _ => {}, //~ ERROR unreachable pattern
- }
- match x {
- _ if false => {}, //~ ERROR unreachable pattern
- }
-}
-
-fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
- match x {} // ok
- match x {
- _ => {}, //~ ERROR unreachable pattern
- }
- match x {
- _ if false => {}, //~ ERROR unreachable pattern
- }
-}
-
-fn never(x: !) {
- match x {} // ok
- match x {
- _ => {}, //~ ERROR unreachable pattern
- }
- match x {
- _ if false => {}, //~ ERROR unreachable pattern
- }
-}
-
-fn main() {
- match None::<!> {
- None => {}
- Some(_) => {} //~ ERROR unreachable pattern
- }
- match None::<EmptyEnum> {
- None => {}
- Some(_) => {} //~ ERROR unreachable pattern
- }
-
- match_empty!(0u8);
- //~^ ERROR type `u8` is non-empty
- match_empty!(NonEmptyStruct(true));
- //~^ ERROR type `NonEmptyStruct` is non-empty
- match_empty!((NonEmptyUnion1 { foo: () }));
- //~^ ERROR type `NonEmptyUnion1` is non-empty
- match_empty!((NonEmptyUnion2 { foo: () }));
- //~^ ERROR type `NonEmptyUnion2` is non-empty
- match_empty!(NonEmptyEnum1::Foo(true));
- //~^ ERROR `Foo(_)` not covered
- match_empty!(NonEmptyEnum2::Foo(true));
- //~^ ERROR `Foo(_)` and `Bar` not covered
- match_empty!(NonEmptyEnum5::V1);
- //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered
-
- match_false!(0u8);
- //~^ ERROR `_` not covered
- match_false!(NonEmptyStruct(true));
- //~^ ERROR `NonEmptyStruct(_)` not covered
- match_false!((NonEmptyUnion1 { foo: () }));
- //~^ ERROR `NonEmptyUnion1 { .. }` not covered
- match_false!((NonEmptyUnion2 { foo: () }));
- //~^ ERROR `NonEmptyUnion2 { .. }` not covered
- match_false!(NonEmptyEnum1::Foo(true));
- //~^ ERROR `Foo(_)` not covered
- match_false!(NonEmptyEnum2::Foo(true));
- //~^ ERROR `Foo(_)` and `Bar` not covered
- match_false!(NonEmptyEnum5::V1);
- //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered
-}
+++ /dev/null
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:52:9
- |
-LL | _ => {},
- | ^
- |
-note: the lint level is defined here
- --> $DIR/match-empty-exhaustive_patterns.rs:5:9
- |
-LL | #![deny(unreachable_patterns)]
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:55:9
- |
-LL | _ if false => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:62:9
- |
-LL | _ => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:65:9
- |
-LL | _ if false => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:72:9
- |
-LL | _ => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:75:9
- |
-LL | _ if false => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:82:9
- |
-LL | Some(_) => {}
- | ^^^^^^^
-
-error: unreachable pattern
- --> $DIR/match-empty-exhaustive_patterns.rs:86:9
- |
-LL | Some(_) => {}
- | ^^^^^^^
-
-error[E0004]: non-exhaustive patterns: type `u8` is non-empty
- --> $DIR/match-empty-exhaustive_patterns.rs:89:18
- |
-LL | match_empty!(0u8);
- | ^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `u8`
-
-error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty
- --> $DIR/match-empty-exhaustive_patterns.rs:91:18
- |
-LL | struct NonEmptyStruct(bool);
- | ---------------------------- `NonEmptyStruct` defined here
-...
-LL | match_empty!(NonEmptyStruct(true));
- | ^^^^^^^^^^^^^^^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyStruct`
-
-error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
- --> $DIR/match-empty-exhaustive_patterns.rs:93:18
- |
-LL | / union NonEmptyUnion1 {
-LL | | foo: (),
-LL | | }
- | |_- `NonEmptyUnion1` defined here
-...
-LL | match_empty!((NonEmptyUnion1 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion1`
-
-error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
- --> $DIR/match-empty-exhaustive_patterns.rs:95:18
- |
-LL | / union NonEmptyUnion2 {
-LL | | foo: (),
-LL | | bar: (),
-LL | | }
- | |_- `NonEmptyUnion2` defined here
-...
-LL | match_empty!((NonEmptyUnion2 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion2`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:97:18
- |
-LL | / enum NonEmptyEnum1 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum1` defined here
-...
-LL | match_empty!(NonEmptyEnum1::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum1`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:99:18
- |
-LL | / enum NonEmptyEnum2 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | Bar,
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum2` defined here
-...
-LL | match_empty!(NonEmptyEnum2::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum2`
-
-error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:101:18
- |
-LL | / enum NonEmptyEnum5 {
-LL | | V1, V2, V3, V4, V5,
-LL | | }
- | |_- `NonEmptyEnum5` defined here
-...
-LL | match_empty!(NonEmptyEnum5::V1);
- | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum5`
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:104:18
- |
-LL | match_false!(0u8);
- | ^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `u8`
-
-error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:106:18
- |
-LL | struct NonEmptyStruct(bool);
- | ---------------------------- `NonEmptyStruct` defined here
-...
-LL | match_false!(NonEmptyStruct(true));
- | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyStruct`
-
-error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:108:18
- |
-LL | / union NonEmptyUnion1 {
-LL | | foo: (),
-LL | | }
- | |_- `NonEmptyUnion1` defined here
-...
-LL | match_false!((NonEmptyUnion1 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion1`
-
-error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:110:18
- |
-LL | / union NonEmptyUnion2 {
-LL | | foo: (),
-LL | | bar: (),
-LL | | }
- | |_- `NonEmptyUnion2` defined here
-...
-LL | match_false!((NonEmptyUnion2 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion2`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:112:18
- |
-LL | / enum NonEmptyEnum1 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum1` defined here
-...
-LL | match_false!(NonEmptyEnum1::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum1`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:114:18
- |
-LL | / enum NonEmptyEnum2 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | Bar,
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum2` defined here
-...
-LL | match_false!(NonEmptyEnum2::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum2`
-
-error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
- --> $DIR/match-empty-exhaustive_patterns.rs:116:18
- |
-LL | / enum NonEmptyEnum5 {
-LL | | V1, V2, V3, V4, V5,
-LL | | }
- | |_- `NonEmptyEnum5` defined here
-...
-LL | match_false!(NonEmptyEnum5::V1);
- | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum5`
-
-error: aborting due to 22 previous errors
-
-For more information about this error, try `rustc --explain E0004`.
+++ /dev/null
-// aux-build:empty.rs
-#![feature(never_type)]
-#![feature(never_type_fallback)]
-#![deny(unreachable_patterns)]
-
-extern crate empty;
-
-enum EmptyEnum {}
-
-struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
-union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
- foo: (),
-}
-union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here
- foo: (),
- bar: (),
-}
-enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here
- Foo(bool),
- //~^ not covered
- //~| not covered
-}
-enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here
- Foo(bool),
- //~^ not covered
- //~| not covered
- Bar,
- //~^ not covered
- //~| not covered
-}
-enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here
- V1, V2, V3, V4, V5,
-}
-
-macro_rules! match_empty {
- ($e:expr) => {
- match $e {}
- };
-}
-macro_rules! match_false {
- ($e:expr) => {
- match $e {
- _ if false => {}
- }
- };
-}
-
-fn empty_enum(x: EmptyEnum) {
- match x {} // ok
- match x {
- _ => {}, //~ ERROR unreachable pattern
- }
- match x {
- _ if false => {}, //~ ERROR unreachable pattern
- }
-}
-
-fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
- match x {} // ok
- match x {
- _ => {}, //~ ERROR unreachable pattern
- }
- match x {
- _ if false => {}, //~ ERROR unreachable pattern
- }
-}
-
-fn never(x: !) {
- match x {} // ok
- match x {
- _ => {}, //~ ERROR unreachable pattern
- }
- match x {
- _ if false => {}, //~ ERROR unreachable pattern
- }
-}
-
-fn main() {
- // `exhaustive_patterns` is not on, so uninhabited branches are not detected as unreachable.
- match None::<!> {
- None => {}
- Some(_) => {}
- }
- match None::<EmptyEnum> {
- None => {}
- Some(_) => {}
- }
-
- match_empty!(0u8);
- //~^ ERROR type `u8` is non-empty
- match_empty!(NonEmptyStruct(true));
- //~^ ERROR type `NonEmptyStruct` is non-empty
- match_empty!((NonEmptyUnion1 { foo: () }));
- //~^ ERROR type `NonEmptyUnion1` is non-empty
- match_empty!((NonEmptyUnion2 { foo: () }));
- //~^ ERROR type `NonEmptyUnion2` is non-empty
- match_empty!(NonEmptyEnum1::Foo(true));
- //~^ ERROR `Foo(_)` not covered
- match_empty!(NonEmptyEnum2::Foo(true));
- //~^ ERROR `Foo(_)` and `Bar` not covered
- match_empty!(NonEmptyEnum5::V1);
- //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered
-
- match_false!(0u8);
- //~^ ERROR `_` not covered
- match_false!(NonEmptyStruct(true));
- //~^ ERROR `NonEmptyStruct(_)` not covered
- match_false!((NonEmptyUnion1 { foo: () }));
- //~^ ERROR `NonEmptyUnion1 { .. }` not covered
- match_false!((NonEmptyUnion2 { foo: () }));
- //~^ ERROR `NonEmptyUnion2 { .. }` not covered
- match_false!(NonEmptyEnum1::Foo(true));
- //~^ ERROR `Foo(_)` not covered
- match_false!(NonEmptyEnum2::Foo(true));
- //~^ ERROR `Foo(_)` and `Bar` not covered
- match_false!(NonEmptyEnum5::V1);
- //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered
-}
+++ /dev/null
-error: unreachable pattern
- --> $DIR/match-empty.rs:51:9
- |
-LL | _ => {},
- | ^
- |
-note: the lint level is defined here
- --> $DIR/match-empty.rs:4:9
- |
-LL | #![deny(unreachable_patterns)]
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: unreachable pattern
- --> $DIR/match-empty.rs:54:9
- |
-LL | _ if false => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty.rs:61:9
- |
-LL | _ => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty.rs:64:9
- |
-LL | _ if false => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty.rs:71:9
- |
-LL | _ => {},
- | ^
-
-error: unreachable pattern
- --> $DIR/match-empty.rs:74:9
- |
-LL | _ if false => {},
- | ^
-
-error[E0004]: non-exhaustive patterns: type `u8` is non-empty
- --> $DIR/match-empty.rs:89:18
- |
-LL | match_empty!(0u8);
- | ^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `u8`
-
-error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty
- --> $DIR/match-empty.rs:91:18
- |
-LL | struct NonEmptyStruct(bool);
- | ---------------------------- `NonEmptyStruct` defined here
-...
-LL | match_empty!(NonEmptyStruct(true));
- | ^^^^^^^^^^^^^^^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyStruct`
-
-error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
- --> $DIR/match-empty.rs:93:18
- |
-LL | / union NonEmptyUnion1 {
-LL | | foo: (),
-LL | | }
- | |_- `NonEmptyUnion1` defined here
-...
-LL | match_empty!((NonEmptyUnion1 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion1`
-
-error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
- --> $DIR/match-empty.rs:95:18
- |
-LL | / union NonEmptyUnion2 {
-LL | | foo: (),
-LL | | bar: (),
-LL | | }
- | |_- `NonEmptyUnion2` defined here
-...
-LL | match_empty!((NonEmptyUnion2 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion2`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
- --> $DIR/match-empty.rs:97:18
- |
-LL | / enum NonEmptyEnum1 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum1` defined here
-...
-LL | match_empty!(NonEmptyEnum1::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum1`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
- --> $DIR/match-empty.rs:99:18
- |
-LL | / enum NonEmptyEnum2 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | Bar,
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum2` defined here
-...
-LL | match_empty!(NonEmptyEnum2::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum2`
-
-error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
- --> $DIR/match-empty.rs:101:18
- |
-LL | / enum NonEmptyEnum5 {
-LL | | V1, V2, V3, V4, V5,
-LL | | }
- | |_- `NonEmptyEnum5` defined here
-...
-LL | match_empty!(NonEmptyEnum5::V1);
- | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum5`
-
-error[E0004]: non-exhaustive patterns: `_` not covered
- --> $DIR/match-empty.rs:104:18
- |
-LL | match_false!(0u8);
- | ^^^ pattern `_` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `u8`
-
-error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
- --> $DIR/match-empty.rs:106:18
- |
-LL | struct NonEmptyStruct(bool);
- | ---------------------------- `NonEmptyStruct` defined here
-...
-LL | match_false!(NonEmptyStruct(true));
- | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyStruct`
-
-error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
- --> $DIR/match-empty.rs:108:18
- |
-LL | / union NonEmptyUnion1 {
-LL | | foo: (),
-LL | | }
- | |_- `NonEmptyUnion1` defined here
-...
-LL | match_false!((NonEmptyUnion1 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion1`
-
-error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
- --> $DIR/match-empty.rs:110:18
- |
-LL | / union NonEmptyUnion2 {
-LL | | foo: (),
-LL | | bar: (),
-LL | | }
- | |_- `NonEmptyUnion2` defined here
-...
-LL | match_false!((NonEmptyUnion2 { foo: () }));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyUnion2`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
- --> $DIR/match-empty.rs:112:18
- |
-LL | / enum NonEmptyEnum1 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum1` defined here
-...
-LL | match_false!(NonEmptyEnum1::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum1`
-
-error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
- --> $DIR/match-empty.rs:114:18
- |
-LL | / enum NonEmptyEnum2 {
-LL | | Foo(bool),
- | | --- not covered
-LL | |
-LL | |
-LL | | Bar,
- | | --- not covered
-LL | |
-LL | |
-LL | | }
- | |_- `NonEmptyEnum2` defined here
-...
-LL | match_false!(NonEmptyEnum2::Foo(true));
- | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum2`
-
-error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
- --> $DIR/match-empty.rs:116:18
- |
-LL | / enum NonEmptyEnum5 {
-LL | | V1, V2, V3, V4, V5,
-LL | | }
- | |_- `NonEmptyEnum5` defined here
-...
-LL | match_false!(NonEmptyEnum5::V1);
- | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
- |
- = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
- = note: the matched value is of type `NonEmptyEnum5`
-
-error: aborting due to 20 previous errors
-
-For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+// check-pass
+// aux-build:empty.rs
+//
+// This tests plays with matching and uninhabited types. This also serves as a test for the
+// `tcx.is_ty_uninhabited_from()` function.
+#![feature(never_type)]
+#![feature(never_type_fallback)]
+#![feature(exhaustive_patterns)]
+#![deny(unreachable_patterns)]
+
+macro_rules! assert_empty {
+ ($ty:ty) => {
+ const _: () = {
+ fn assert_empty(x: $ty) {
+ match x {}
+ match Some(x) {
+ None => {}
+ }
+ }
+ };
+ };
+}
+macro_rules! assert_non_empty {
+ ($ty:ty) => {
+ const _: () = {
+ fn assert_non_empty(x: $ty) {
+ match x {
+ _ => {}
+ }
+ match Some(x) {
+ None => {}
+ Some(_) => {}
+ }
+ }
+ };
+ };
+}
+
+extern crate empty;
+assert_empty!(empty::EmptyForeignEnum);
+assert_empty!(empty::VisiblyUninhabitedForeignStruct);
+assert_non_empty!(empty::SecretlyUninhabitedForeignStruct);
+
+enum Void {}
+assert_empty!(Void);
+
+enum Enum2 {
+ Foo(Void),
+ Bar(!),
+}
+assert_empty!(Enum2);
+
+enum Enum3 {
+ Foo(Void),
+ Bar {
+ x: u64,
+ y: !,
+ },
+}
+assert_empty!(Enum3);
+
+enum Enum4 {
+ Foo(u64),
+ Bar(!),
+}
+assert_non_empty!(Enum4);
+
+struct Struct1(empty::EmptyForeignEnum);
+assert_empty!(Struct1);
+
+struct Struct2 {
+ x: u64,
+ y: !,
+}
+assert_empty!(Struct2);
+
+union Union {
+ foo: !,
+}
+assert_non_empty!(Union);
+
+assert_empty!((!, String));
+
+assert_non_empty!(&'static !);
+assert_non_empty!(&'static Struct1);
+assert_non_empty!(&'static &'static &'static !);
+
+assert_empty!([!; 1]);
+assert_empty!([Void; 2]);
+assert_non_empty!([!; 0]);
+assert_non_empty!(&'static [!]);
+
+mod visibility {
+ /// This struct can only be seen to be inhabited in modules `b`, `c` or `d`, because otherwise
+ /// the uninhabitedness of both `SecretlyUninhabited` structs is hidden.
+ struct SometimesEmptyStruct {
+ x: a::b::SecretlyUninhabited,
+ y: c::AlsoSecretlyUninhabited,
+ }
+
+ /// This enum can only be seen to be inhabited in module `d`.
+ enum SometimesEmptyEnum {
+ X(c::AlsoSecretlyUninhabited),
+ Y(c::d::VerySecretlyUninhabited),
+ }
+
+ mod a {
+ use super::*;
+ pub mod b {
+ use super::*;
+ pub struct SecretlyUninhabited {
+ _priv: !,
+ }
+ assert_empty!(SometimesEmptyStruct);
+ }
+
+ assert_non_empty!(SometimesEmptyStruct);
+ assert_non_empty!(SometimesEmptyEnum);
+ }
+
+ mod c {
+ use super::*;
+ pub struct AlsoSecretlyUninhabited {
+ _priv: ::Struct1,
+ }
+ assert_empty!(SometimesEmptyStruct);
+ assert_non_empty!(SometimesEmptyEnum);
+
+ pub mod d {
+ use super::*;
+ pub struct VerySecretlyUninhabited {
+ _priv: !,
+ }
+ assert_empty!(SometimesEmptyStruct);
+ assert_empty!(SometimesEmptyEnum);
+ }
+ }
+
+ assert_non_empty!(SometimesEmptyStruct);
+ assert_non_empty!(SometimesEmptyEnum);
+}
+
+fn main() {}
--- /dev/null
+struct Foo(bool);
+fn foo(_: usize) -> Foo { Foo(false) }
+
+fn main() {
+ match Foo(true) {
+ foo(x) //~ ERROR expected tuple struct or tuple variant, found function `foo`
+ => ()
+ }
+}
--- /dev/null
+error[E0532]: expected tuple struct or tuple variant, found function `foo`
+ --> $DIR/issue-10200.rs:6:9
+ |
+LL | struct Foo(bool);
+ | ----------------- similarly named tuple struct `Foo` defined here
+...
+LL | foo(x)
+ | ^^^ help: a tuple struct with a similar name exists (notice the capitalization): `Foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0532`.
LL | for j in a {
| ^
| |
- | value moved here, in previous iteration of loop
+ | `a` moved due to this implicit call to `.into_iter()`, in previous iteration of loop
| help: consider borrowing to avoid moving into the for loop: `&a`
+ |
+note: this function takes ownership of the receiver `self`, which moves `a`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | fn into_iter(self) -> Self::IntoIter;
+ | ^^^^
error: aborting due to 2 previous errors
--- /dev/null
+// Test for #78438: ensure underline alignment with many tabs on the left, long line on the right
+
+// ignore-tidy-linelength
+// ignore-tidy-tab
+
+ fn main() {
+ let money = 42i32;
+ match money {
+ v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
+ //~^ ERROR variable `v` is not bound in all patterns
+ v => println!("Enough money {}", v),
+ }
+ }
--- /dev/null
+error[E0408]: variable `v` is not bound in all patterns
+ --> $DIR/tabs-trimming.rs:9:16
+ |
+LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
+ | - ^ ^ pattern doesn't bind `v`
+ | | |
+ | | pattern doesn't bind `v`
+ | variable not in all patterns
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0408`.
|
= note: expected type `for<'r> Fn<(&'r X,)>`
found type `Fn<(&'static X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error[E0308]: mismatched types
--> $DIR/issue-57611-trait-alias.rs:17:16
|
= note: expected type `FnOnce<(&X,)>`
found type `FnOnce<(&'static X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error: aborting due to 4 previous errors
|
= note: expected type `FnOnce<(&X,)>`
found type `FnOnce<(&X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error[E0308]: mismatched types
--> $DIR/issue-57611-trait-alias.rs:17:16
|
= note: expected type `for<'r> Fn<(&'r X,)>`
found type `Fn<(&'<empty> X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error[E0308]: mismatched types
--> $DIR/issue-57611-trait-alias.rs:17:16
|
= note: expected type `FnOnce<(&X,)>`
found type `FnOnce<(&'<empty> X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error[E0308]: mismatched types
--> $DIR/issue-57611-trait-alias.rs:17:16
|
= note: expected type `for<'r> Fn<(&'r X,)>`
found type `Fn<(&'<empty> X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error[E0308]: mismatched types
--> $DIR/issue-57611-trait-alias.rs:17:16
|
= note: expected type `FnOnce<(&X,)>`
found type `FnOnce<(&'<empty> X,)>`
+note: this closure does not fulfill the lifetime requirements
+ --> $DIR/issue-57611-trait-alias.rs:25:9
+ |
+LL | |x| x
+ | ^^^^^
error: aborting due to 5 previous errors
--> $DIR/issue-30906.rs:15:5
|
LL | test(Compose(f, |_| {}));
- | ^^^^ one type is more general than the other
+ | ^^^^ lifetime mismatch
|
= note: expected type `FnOnce<(&'x str,)>`
found type `FnOnce<(&str,)>`
+note: the lifetime requirement is introduced here
+ --> $DIR/issue-30906.rs:3:12
+ |
+LL | fn test<F: for<'x> FnOnce<(&'x str,)>>(_: F) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
LL | println!("{}", &y);
| ^^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `y`
+note: this function takes ownership of the receiver `self`, which moves `y`
--> $DIR/borrow-after-move.rs:5:12
|
LL | fn foo(self) -> String;
LL | y.foo();
| ^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `y`
+note: this function takes ownership of the receiver `self`, which moves `y`
--> $DIR/double-move.rs:5:12
|
LL | fn foo(self) -> String;
LL | return self.x;
| ^^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `self`
+note: this function takes ownership of the receiver `self`, which moves `self`
--> $DIR/use-after-move-self-based-on-type.rs:15:16
|
LL | pub fn bar(self) {}
LL | return *self.x;
| ^^^^^^^ value used here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `self`
+note: this function takes ownership of the receiver `self`, which moves `self`
--> $DIR/use-after-move-self.rs:13:16
|
LL | pub fn bar(self) {}
LL | println!("{}", start.test);
| ^^^^^^^^^^ value borrowed here after move
|
-note: this function consumes the receiver `self` by taking ownership of it, which moves `start`
+note: this function takes ownership of the receiver `self`, which moves `start`
--> $DIR/walk-struct-literal-with.rs:7:28
|
LL | fn make_string_bar(mut self) -> Mine{