From 3f61742cc0dee504f050bb9fdbf801e274780a29 Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Mon, 15 Jan 2018 16:42:44 +0300 Subject: [PATCH 01/70] Fix no_integrated_as option to work with new codegen architecture. Old implementation called the assembler once per crate, but we need to call it for each object file instead, because a single crate can now have more than one object file. This patch fixes issue #45836 (Can't compile core for msp430 in release mode) --- src/librustc_trans/back/command.rs | 1 + src/librustc_trans/back/write.rs | 109 +++++++++++++++-------------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs index ea68e3b28b668..024f80f050f51 100644 --- a/src/librustc_trans/back/command.rs +++ b/src/librustc_trans/back/command.rs @@ -16,6 +16,7 @@ use std::fmt; use std::io; use std::process::{self, Output, Child}; +#[derive(Clone)] pub struct Command { program: OsString, args: Vec, diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 1ee04a46243a2..d401d68783aa4 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -11,6 +11,7 @@ use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; use back::lto::{self, ModuleBuffer, ThinBuffer}; use back::link::{self, get_linker, remove}; +use back::command::Command; use back::linker::LinkerInfo; use back::symbol_export::ExportedSymbols; use base; @@ -18,7 +19,7 @@ use consts; use rustc_incremental::{save_trans_partition, in_incr_comp_dir}; use rustc::dep_graph::{DepGraph, WorkProductFileKind}; use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; -use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses, +use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses, AllPasses, Sanitizer}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; @@ -32,7 +33,7 @@ use CrateInfo; use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::ty::TyCtxt; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr, print_time_passes_entry}; -use rustc::util::fs::{link_or_copy, rename_or_copy_remove}; +use rustc::util::fs::{link_or_copy}; use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; use errors::emitter::{Emitter}; use syntax::attr; @@ -258,6 +259,7 @@ pub struct ModuleConfig { // make the object file bitcode. Provides easy compatibility with // emscripten's ecc compiler, when used as the linker. obj_is_bitcode: bool, + no_integrated_as: bool, } impl ModuleConfig { @@ -275,6 +277,7 @@ impl ModuleConfig { emit_asm: false, emit_obj: false, obj_is_bitcode: false, + no_integrated_as: false, no_verify: false, no_prepopulate_passes: false, @@ -313,6 +316,12 @@ impl ModuleConfig { } } +/// Assembler name and command used by codegen when no_integrated_as is enabled +struct AssemblerCommand { + name: PathBuf, + cmd: Command, +} + /// Additional resources used by optimize_and_codegen (not module specific) #[derive(Clone)] pub struct CodegenContext { @@ -356,6 +365,8 @@ pub struct CodegenContext { // A reference to the TimeGraph so we can register timings. None means that // measuring is disabled. time_graph: Option, + // The assembler command if no_integrated_as option is enabled, None otherwise + assembler_cmd: Option>, } impl CodegenContext { @@ -639,13 +650,17 @@ unsafe fn codegen(cgcx: &CodegenContext, !cgcx.crate_types.contains(&config::CrateTypeRlib) && mtrans.kind == ModuleKind::Regular; + // If we don't have the integrated assembler, then we need to emit asm + // from LLVM and use `gcc` to create the object file. + let asm_to_obj = config.emit_obj && config.no_integrated_as; + // Change what we write and cleanup based on whether obj files are // just llvm bitcode. In that case write bitcode, and possibly // delete the bitcode if it wasn't requested. Don't generate the // machine code, instead copy the .o file from the .bc let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm); let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm; - let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm; + let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm && !asm_to_obj; let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm; let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); @@ -725,7 +740,7 @@ unsafe fn codegen(cgcx: &CodegenContext, timeline.record("ir"); } - if config.emit_asm || (asm2wasm && config.emit_obj) { + if config.emit_asm || (asm2wasm && config.emit_obj) || asm_to_obj { let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); // We can't use the same module for asm and binary output, because that triggers @@ -760,6 +775,14 @@ unsafe fn codegen(cgcx: &CodegenContext, llvm::FileType::ObjectFile) })?; timeline.record("obj"); + } else if asm_to_obj { + let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + run_assembler(cgcx, diag_handler, &assembly, &obj_out); + timeline.record("asm_to_obj"); + + if !config.emit_asm && !cgcx.save_temps { + drop(fs::remove_file(&assembly)); + } } Ok(()) @@ -841,7 +864,6 @@ pub fn start_async_translation(tcx: TyCtxt, total_cgus: usize) -> OngoingCrateTranslation { let sess = tcx.sess; - let crate_output = tcx.output_filenames(LOCAL_CRATE); let crate_name = tcx.crate_name(LOCAL_CRATE); let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins"); let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs, @@ -855,19 +877,9 @@ pub fn start_async_translation(tcx: TyCtxt, subsystem.to_string() }); - let no_integrated_as = tcx.sess.opts.cg.no_integrated_as || - (tcx.sess.target.target.options.no_integrated_as && - (crate_output.outputs.contains_key(&OutputType::Object) || - crate_output.outputs.contains_key(&OutputType::Exe))); let linker_info = LinkerInfo::new(tcx); let crate_info = CrateInfo::new(tcx); - let output_types_override = if no_integrated_as { - OutputTypes::new(&[(OutputType::Assembly, None)]) - } else { - sess.opts.output_types.clone() - }; - // Figure out what we actually need to build. let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); let mut metadata_config = ModuleConfig::new(vec![]); @@ -913,7 +925,10 @@ pub fn start_async_translation(tcx: TyCtxt, allocator_config.emit_bc_compressed = true; } - for output_type in output_types_override.keys() { + modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as || + tcx.sess.target.target.options.no_integrated_as; + + for output_type in sess.opts.output_types.keys() { match *output_type { OutputType::Bitcode => { modules_config.emit_bc = true; } OutputType::LlvmAssembly => { modules_config.emit_ir = true; } @@ -976,7 +991,6 @@ pub fn start_async_translation(tcx: TyCtxt, metadata, windows_subsystem, linker_info, - no_integrated_as, crate_info, time_graph, @@ -1389,6 +1403,18 @@ fn start_executing_work(tcx: TyCtxt, let wasm_import_memory = attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory"); + let assembler_cmd = if modules_config.no_integrated_as { + // HACK: currently we use linker (gcc) as our assembler + let (name, mut cmd, _) = get_linker(sess); + cmd.args(&sess.target.target.options.asm_args); + Some(Arc::new(AssemblerCommand { + name, + cmd, + })) + } else { + None + }; + let cgcx = CodegenContext { crate_types: sess.crate_types.borrow().clone(), each_linked_rlib_for_lto, @@ -1428,6 +1454,7 @@ fn start_executing_work(tcx: TyCtxt, binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen, debuginfo: tcx.sess.opts.debuginfo, wasm_import_memory: wasm_import_memory, + assembler_cmd, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1936,15 +1963,14 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { }); } -pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { - let (pname, mut cmd, _) = get_linker(sess); +pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) { + let assembler = cgcx.assembler_cmd + .as_ref() + .expect("cgcx.assembler_cmd is missing?"); - for arg in &sess.target.target.options.asm_args { - cmd.arg(arg); - } - - cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object)) - .arg(&outputs.temp_path(OutputType::Assembly, None)); + let pname = &assembler.name; + let mut cmd = assembler.cmd.clone(); + cmd.arg("-c").arg("-o").arg(object).arg(assembly); debug!("{:?}", cmd); match cmd.output() { @@ -1953,18 +1979,18 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { let mut note = prog.stderr.clone(); note.extend_from_slice(&prog.stdout); - sess.struct_err(&format!("linking with `{}` failed: {}", - pname.display(), - prog.status)) + handler.struct_err(&format!("linking with `{}` failed: {}", + pname.display(), + prog.status)) .note(&format!("{:?}", &cmd)) .note(str::from_utf8(¬e[..]).unwrap()) .emit(); - sess.abort_if_errors(); + handler.abort_if_errors(); } }, Err(e) => { - sess.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); - sess.abort_if_errors(); + handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); + handler.abort_if_errors(); } } } @@ -2138,7 +2164,6 @@ pub struct OngoingCrateTranslation { metadata: EncodedMetadata, windows_subsystem: Option, linker_info: LinkerInfo, - no_integrated_as: bool, crate_info: CrateInfo, time_graph: Option, coordinator_send: Sender>, @@ -2194,26 +2219,6 @@ impl OngoingCrateTranslation { metadata_module: compiled_modules.metadata_module, }; - if self.no_integrated_as { - run_assembler(sess, &self.output_filenames); - - // HACK the linker expects the object file to be named foo.0.o but - // `run_assembler` produces an object named just foo.o. Rename it if we - // are going to build an executable - if sess.opts.output_types.contains_key(&OutputType::Exe) { - let f = self.output_filenames.path(OutputType::Object); - rename_or_copy_remove(&f, - f.with_file_name(format!("{}.0.o", - f.file_stem().unwrap().to_string_lossy()))).unwrap(); - } - - // Remove assembly source, unless --save-temps was specified - if !sess.opts.cg.save_temps { - fs::remove_file(&self.output_filenames - .temp_path(OutputType::Assembly, None)).unwrap(); - } - } - trans } From 98dee04baefedc3b00077789edceface65ebc343 Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Tue, 16 Jan 2018 16:00:31 +0300 Subject: [PATCH 02/70] Add run-make test for no_integrated_as flag. --- src/test/run-make/no-integrated-as/Makefile | 7 +++++++ src/test/run-make/no-integrated-as/hello.rs | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/test/run-make/no-integrated-as/Makefile create mode 100644 src/test/run-make/no-integrated-as/hello.rs diff --git a/src/test/run-make/no-integrated-as/Makefile b/src/test/run-make/no-integrated-as/Makefile new file mode 100644 index 0000000000000..78e3025b99ad4 --- /dev/null +++ b/src/test/run-make/no-integrated-as/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: +ifeq ($(TARGET),x86_64-unknown-linux-gnu) + $(RUSTC) hello.rs -C no_integrated_as + $(call RUN,hello) +endif diff --git a/src/test/run-make/no-integrated-as/hello.rs b/src/test/run-make/no-integrated-as/hello.rs new file mode 100644 index 0000000000000..68e7f6d94d139 --- /dev/null +++ b/src/test/run-make/no-integrated-as/hello.rs @@ -0,0 +1,13 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("Hello, world!"); +} From 9b95e914a1f6537563b885c2ffbdb069e64cc14c Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 15 Jan 2018 01:48:19 +0200 Subject: [PATCH 03/70] rustc_trans: ignore trailing padding larger than 8 bytes. --- src/librustc_trans/cabi_x86_64.rs | 12 ++++++------ src/test/run-pass/align-with-extern-c-fn.rs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index 6db18bfecf258..681b7f5527332 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -179,12 +179,12 @@ fn cast_target(cls: &[Class], size: Size) -> CastTarget { let mut i = 0; let lo = reg_component(cls, &mut i, size).unwrap(); let offset = Size::from_bytes(8) * (i as u64); - let target = if size <= offset { - CastTarget::from(lo) - } else { - let hi = reg_component(cls, &mut i, size - offset).unwrap(); - CastTarget::Pair(lo, hi) - }; + let mut target = CastTarget::from(lo); + if size > offset { + if let Some(hi) = reg_component(cls, &mut i, size - offset) { + target = CastTarget::Pair(lo, hi); + } + } assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None); target } diff --git a/src/test/run-pass/align-with-extern-c-fn.rs b/src/test/run-pass/align-with-extern-c-fn.rs index db25960df4c87..15e3b4b03eb27 100644 --- a/src/test/run-pass/align-with-extern-c-fn.rs +++ b/src/test/run-pass/align-with-extern-c-fn.rs @@ -14,10 +14,10 @@ #![feature(attr_literals)] #[repr(align(16))] -pub struct A { - y: i64, -} +pub struct A(i64); pub extern "C" fn foo(x: A) {} -fn main() {} +fn main() { + foo(A(0)); +} From cbeebc5b2cde6cb4f6cec9cd0cdfbd5d2c15dc39 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 15 Jan 2018 01:18:48 +0200 Subject: [PATCH 04/70] rustc_trans: take into account primitives larger than 8 bytes. --- src/librustc_trans/cabi_x86_64.rs | 98 ++++++++++++++----------------- src/test/run-pass/issue-38763.rs | 21 +++++++ 2 files changed, 64 insertions(+), 55 deletions(-) create mode 100644 src/test/run-pass/issue-38763.rs diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index 6db18bfecf258..630762602d0ac 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -16,9 +16,11 @@ use context::CodegenCx; use rustc::ty::layout::{self, TyLayout, Size}; -#[derive(Clone, Copy, PartialEq, Debug)] +/// Classification of "eightbyte" components. +// NB: the order of the variants is from general to specific, +// such that `unify(a, b)` is the "smaller" of `a` and `b`. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] enum Class { - None, Int, Sse, SseUp @@ -32,29 +34,10 @@ const LARGEST_VECTOR_SIZE: usize = 512; const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64; fn classify_arg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &ArgType<'tcx>) - -> Result<[Class; MAX_EIGHTBYTES], Memory> { - fn unify(cls: &mut [Class], - off: Size, - c: Class) { - let i = (off.bytes() / 8) as usize; - let to_write = match (cls[i], c) { - (Class::None, _) => c, - (_, Class::None) => return, - - (Class::Int, _) | - (_, Class::Int) => Class::Int, - - (Class::Sse, _) | - (_, Class::Sse) => Class::Sse, - - (Class::SseUp, Class::SseUp) => Class::SseUp - }; - cls[i] = to_write; - } - + -> Result<[Option; MAX_EIGHTBYTES], Memory> { fn classify<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, layout: TyLayout<'tcx>, - cls: &mut [Class], + cls: &mut [Option], off: Size) -> Result<(), Memory> { if !off.is_abi_aligned(layout.align) { @@ -64,31 +47,20 @@ fn classify_arg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &ArgType<'tcx>) return Ok(()); } - match layout.abi { - layout::Abi::Uninhabited => {} + let mut c = match layout.abi { + layout::Abi::Uninhabited => return Ok(()), layout::Abi::Scalar(ref scalar) => { - let reg = match scalar.value { + match scalar.value { layout::Int(..) | layout::Pointer => Class::Int, layout::F32 | layout::F64 => Class::Sse - }; - unify(cls, off, reg); - } - - layout::Abi::Vector { ref element, count } => { - unify(cls, off, Class::Sse); - - // everything after the first one is the upper - // half of a register. - let stride = element.value.size(cx); - for i in 1..count { - let field_off = off + stride * i; - unify(cls, field_off, Class::SseUp); } } + layout::Abi::Vector { .. } => Class::Sse, + layout::Abi::ScalarPair(..) | layout::Abi::Aggregate { .. } => { match layout.variants { @@ -97,12 +69,26 @@ fn classify_arg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &ArgType<'tcx>) let field_off = off + layout.fields.offset(i); classify(cx, layout.field(cx, i), cls, field_off)?; } + return Ok(()); } layout::Variants::Tagged { .. } | layout::Variants::NicheFilling { .. } => return Err(Memory), } } + }; + + // Fill in `cls` for scalars (Int/Sse) and vectors (Sse). + let first = (off.bytes() / 8) as usize; + let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize; + for cls in &mut cls[first..=last] { + *cls = Some(cls.map_or(c, |old| old.min(c))); + + // Everything after the first Sse "eightbyte" + // component is the upper half of a register. + if c == Class::Sse { + c = Class::SseUp; + } } Ok(()) @@ -113,23 +99,23 @@ fn classify_arg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &ArgType<'tcx>) return Err(Memory); } - let mut cls = [Class::None; MAX_EIGHTBYTES]; + let mut cls = [None; MAX_EIGHTBYTES]; classify(cx, arg.layout, &mut cls, Size::from_bytes(0))?; if n > 2 { - if cls[0] != Class::Sse { + if cls[0] != Some(Class::Sse) { return Err(Memory); } - if cls[1..n].iter().any(|&c| c != Class::SseUp) { + if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) { return Err(Memory); } } else { let mut i = 0; while i < n { - if cls[i] == Class::SseUp { - cls[i] = Class::Sse; - } else if cls[i] == Class::Sse { + if cls[i] == Some(Class::SseUp) { + cls[i] = Some(Class::Sse); + } else if cls[i] == Some(Class::Sse) { i += 1; - while i != n && cls[i] == Class::SseUp { i += 1; } + while i != n && cls[i] == Some(Class::SseUp) { i += 1; } } else { i += 1; } @@ -139,14 +125,14 @@ fn classify_arg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &ArgType<'tcx>) Ok(cls) } -fn reg_component(cls: &[Class], i: &mut usize, size: Size) -> Option { +fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option { if *i >= cls.len() { return None; } match cls[*i] { - Class::None => None, - Class::Int => { + None => None, + Some(Class::Int) => { *i += 1; Some(match size.bytes() { 1 => Reg::i8(), @@ -156,8 +142,10 @@ fn reg_component(cls: &[Class], i: &mut usize, size: Size) -> Option { _ => Reg::i64() }) } - Class::Sse => { - let vec_len = 1 + cls[*i+1..].iter().take_while(|&&c| c == Class::SseUp).count(); + Some(Class::Sse) => { + let vec_len = 1 + cls[*i+1..].iter() + .take_while(|&&c| c == Some(Class::SseUp)) + .count(); *i += vec_len; Some(if vec_len == 1 { match size.bytes() { @@ -171,11 +159,11 @@ fn reg_component(cls: &[Class], i: &mut usize, size: Size) -> Option { } }) } - c => bug!("reg_component: unhandled class {:?}", c) + Some(c) => bug!("reg_component: unhandled class {:?}", c) } } -fn cast_target(cls: &[Class], size: Size) -> CastTarget { +fn cast_target(cls: &[Option], size: Size) -> CastTarget { let mut i = 0; let lo = reg_component(cls, &mut i, size).unwrap(); let offset = Size::from_bytes(8) * (i as u64); @@ -203,8 +191,8 @@ pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tc Ok(ref cls) if is_arg => { for &c in cls { match c { - Class::Int => needed_int += 1, - Class::Sse => needed_sse += 1, + Some(Class::Int) => needed_int += 1, + Some(Class::Sse) => needed_sse += 1, _ => {} } } diff --git a/src/test/run-pass/issue-38763.rs b/src/test/run-pass/issue-38763.rs new file mode 100644 index 0000000000000..4bf9513d64f92 --- /dev/null +++ b/src/test/run-pass/issue-38763.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(i128_type)] + +#[repr(C)] +pub struct Foo(i128); + +#[no_mangle] +pub extern "C" fn foo(x: Foo) -> Foo { x } + +fn main() { + foo(Foo(1)); +} From 072c3daa4cb6e1a8958ad9d61ebc7e062d570eac Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jan 2018 17:19:23 -0500 Subject: [PATCH 05/70] track recursion limit when expanding existential impl trait --- src/librustc/infer/region_constraints/mod.rs | 2 +- src/librustc/traits/project.rs | 16 +++++++- .../borrow_check/nll/type_check/mod.rs | 3 ++ .../infinite-impl-trait-issue-38064.rs | 39 +++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/test/compile-fail/impl-trait/infinite-impl-trait-issue-38064.rs diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 72740dd40be29..68d81a2dee352 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -82,7 +82,7 @@ pub type VarOrigins = IndexVec; /// Describes constraints between the region variables and other /// regions, as well as other conditions that must be verified, or /// assumptions that can be made. -#[derive(Default)] +#[derive(Debug, Default)] pub struct RegionConstraintData<'tcx> { /// Constraints of the form `A <= B`, where either `A` or `B` can /// be a region variable (or neither, as it happens). diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 3342d13dd6e5f..d34649782ba6a 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -293,9 +293,23 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, Reveal::UserFacing => ty, Reveal::All => { + let recursion_limit = self.tcx().sess.recursion_limit.get(); + if self.depth >= recursion_limit { + let obligation = Obligation::with_depth( + self.cause.clone(), + recursion_limit, + self.param_env, + ty, + ); + self.selcx.infcx().report_overflow_error(&obligation, true); + } + let generic_ty = self.tcx().type_of(def_id); let concrete_ty = generic_ty.subst(self.tcx(), substs); - self.fold_ty(concrete_ty) + self.depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.depth -= 1; + folded_ty } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 901b73c610e3b..9dcd4435580ab 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -681,6 +681,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let data = self.infcx.take_and_reset_region_constraints(); if !data.is_empty() { + debug!("fully_perform_op: constraints generated at {:?} are {:#?}", + locations, data); self.constraints .outlives_sets .push(OutlivesSet { locations, data }); @@ -1539,6 +1541,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { where T: fmt::Debug + TypeFoldable<'tcx>, { + debug!("normalize(value={:?}, location={:?})", value, location); self.fully_perform_op(location.at_self(), |this| { let mut selcx = traits::SelectionContext::new(this.infcx); let cause = this.misc(this.last_span); diff --git a/src/test/compile-fail/impl-trait/infinite-impl-trait-issue-38064.rs b/src/test/compile-fail/impl-trait/infinite-impl-trait-issue-38064.rs new file mode 100644 index 0000000000000..abde9689bd6b6 --- /dev/null +++ b/src/test/compile-fail/impl-trait/infinite-impl-trait-issue-38064.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that attempts to construct infinite types via impl trait fail +// in a graceful way. +// +// Regression test for #38064. + +// error-pattern:overflow evaluating the requirement `impl Quux` + +#![feature(conservative_impl_trait)] + +trait Quux {} + +fn foo() -> impl Quux { + struct Foo(T); + impl Quux for Foo {} + Foo(bar()) +} + +fn bar() -> impl Quux { + struct Bar(T); + impl Quux for Bar {} + Bar(foo()) +} + +// effectively: +// struct Foo(Bar); +// struct Bar(Foo); +// should produce an error about infinite size + +fn main() { foo(); } From 8447f4fc0fe4e497b9e914f8dc81b3134efaf6a4 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 13 Jan 2018 15:55:38 +0000 Subject: [PATCH 06/70] Add CGU size heuristic for partitioning This addresses the concern of #47316 by estimating CGU size based on the size of its MIR. Looking at the size estimate differences for a small selection of crates, this heuristic produces different orderings, which should more accurately reflect optimisation time. Fixes #47316. --- src/librustc_mir/monomorphize/partitioning.rs | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index e899cc072e072..d8ec074b8a464 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -110,11 +110,12 @@ use rustc::mir::mono::{Linkage, Visibility}; use rustc::ty::{self, TyCtxt, InstanceDef}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use std::collections::hash_map::Entry; +use std::collections::hash_map::{HashMap, Entry}; use syntax::ast::NodeId; use syntax::symbol::{Symbol, InternedString}; use rustc::mir::mono::MonoItem; use monomorphize::item::{MonoItemExt, InstantiationMode}; +use core::usize; pub use rustc::mir::mono::CodegenUnit; @@ -229,7 +230,7 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // If the partitioning should produce a fixed count of codegen units, merge // until that count is reached. if let PartitioningStrategy::FixedUnitCount(count) = strategy { - merge_codegen_units(&mut initial_partitioning, count, &tcx.crate_name.as_str()); + merge_codegen_units(tcx, &mut initial_partitioning, count, &tcx.crate_name.as_str()); debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); } @@ -404,7 +405,8 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<'tcx>, +fn merge_codegen_units<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + initial_partitioning: &mut PreInliningPartitioning<'tcx>, target_cgu_count: usize, crate_name: &str) { assert!(target_cgu_count >= 1); @@ -421,12 +423,48 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< // the stable sort below will keep everything nice and deterministic. codegen_units.sort_by_key(|cgu| cgu.name().clone()); + // Estimate the size of a codegen unit as (approximately) the number of MIR + // statements it corresponds to. + fn codegen_unit_size_estimate<'a, 'tcx>(cgu: &CodegenUnit<'tcx>, + mono_item_sizes: &HashMap) + -> usize { + cgu.items().keys().map(|mi| mono_item_sizes.get(mi).unwrap()).sum() + } + + fn mono_item_size_estimate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + item: &MonoItem<'tcx>) + -> usize { + match item { + MonoItem::Fn(instance) => { + // Estimate the size of a function based on how many statements + // it contains. + let mir = tcx.instance_mir(instance.def); + mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() + }, + // Conservatively estimate the size of a static declaration + // or assembly to be 1. + MonoItem::Static(_) | MonoItem::GlobalAsm(_) => 1, + } + } + + // Since `sort_by_key` currently recomputes the keys for each comparison, + // we can save unnecessary recomputations by storing size estimates for + // each `MonoItem`. Storing estimates for `CodegenUnit` might be preferable, + // but its structure makes it awkward to use as a key and additionally their + // sizes change as the merging occurs, requiring the map to be updated. + let mut sizes: HashMap = HashMap::new(); + for mis in codegen_units.iter().map(|cgu| cgu.items().keys()) { + mis.for_each(|mi| { + sizes.entry(*mi).or_insert_with(|| mono_item_size_estimate(tcx, mi)); + }); + } + // Merge the two smallest codegen units until the target size is reached. // Note that "size" is estimated here rather inaccurately as the number of // translation items in a given unit. This could be improved on. while codegen_units.len() > target_cgu_count { // Sort small cgus to the back - codegen_units.sort_by_key(|cgu| -(cgu.items().len() as i64)); + codegen_units.sort_by_key(|cgu| usize::MAX - codegen_unit_size_estimate(cgu, &sizes)); let mut smallest = codegen_units.pop().unwrap(); let second_smallest = codegen_units.last_mut().unwrap(); From e60b0f802b438d350ccd1ec1b42f515994b1a4c0 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 15 Jan 2018 18:28:34 +0000 Subject: [PATCH 07/70] Refactor CodegenUnit size estimates --- src/librustc/mir/mono.rs | 40 ++++++++++++++- src/librustc_mir/monomorphize/partitioning.rs | 50 ++++--------------- src/librustc_trans/base.rs | 4 +- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index efdf4066815f4..bbef045a305a5 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -10,7 +10,7 @@ use syntax::ast::NodeId; use syntax::symbol::InternedString; -use ty::Instance; +use ty::{Instance, TyCtxt}; use util::nodemap::FxHashMap; use rustc_data_structures::base_n; use rustc_data_structures::stable_hasher::{HashStable, StableHasherResult, @@ -25,6 +25,22 @@ pub enum MonoItem<'tcx> { GlobalAsm(NodeId), } +impl<'tcx> MonoItem<'tcx> { + pub fn size_estimate<'a>(&self, tcx: &TyCtxt<'a, 'tcx, 'tcx>) -> usize { + match *self { + MonoItem::Fn(instance) => { + // Estimate the size of a function based on how many statements + // it contains. + let mir = tcx.instance_mir(instance.def); + mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() + }, + // Conservatively estimate the size of a static declaration + // or assembly to be 1. + MonoItem::Static(_) | MonoItem::GlobalAsm(_) => 1, + } + } +} + impl<'tcx> HashStable> for MonoItem<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, @@ -52,6 +68,7 @@ pub struct CodegenUnit<'tcx> { /// as well as the crate name and disambiguator. name: InternedString, items: FxHashMap, (Linkage, Visibility)>, + size_estimate: Option, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -101,6 +118,7 @@ impl<'tcx> CodegenUnit<'tcx> { CodegenUnit { name: name, items: FxHashMap(), + size_estimate: None, } } @@ -131,6 +149,25 @@ impl<'tcx> CodegenUnit<'tcx> { let hash = hash & ((1u128 << 80) - 1); base_n::encode(hash, base_n::CASE_INSENSITIVE) } + + pub fn estimate_size<'a>(&mut self, tcx: &TyCtxt<'a, 'tcx, 'tcx>) { + // Estimate the size of a codegen unit as (approximately) the number of MIR + // statements it corresponds to. + self.size_estimate = Some(self.items.keys().map(|mi| mi.size_estimate(tcx)).sum()); + } + + pub fn size_estimate(&self) -> usize { + // Should only be called if `estimate_size` has previously been called. + assert!(self.size_estimate.is_some()); + self.size_estimate.unwrap() + } + + pub fn modify_size_estimate(&mut self, delta: usize) { + assert!(self.size_estimate.is_some()); + if let Some(size_estimate) = self.size_estimate { + self.size_estimate = Some(size_estimate + delta); + } + } } impl<'tcx> HashStable> for CodegenUnit<'tcx> { @@ -140,6 +177,7 @@ impl<'tcx> HashStable> for CodegenUnit<'tcx> { let CodegenUnit { ref items, name, + .. } = *self; name.hash_stable(hcx, hasher); diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index d8ec074b8a464..4150f9f95485b 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -110,7 +110,7 @@ use rustc::mir::mono::{Linkage, Visibility}; use rustc::ty::{self, TyCtxt, InstanceDef}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use std::collections::hash_map::{HashMap, Entry}; +use std::collections::hash_map::Entry; use syntax::ast::NodeId; use syntax::symbol::{Symbol, InternedString}; use rustc::mir::mono::MonoItem; @@ -225,12 +225,14 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut initial_partitioning = place_root_translation_items(tcx, trans_items); + initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(&tcx)); + debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter()); // If the partitioning should produce a fixed count of codegen units, merge // until that count is reached. if let PartitioningStrategy::FixedUnitCount(count) = strategy { - merge_codegen_units(tcx, &mut initial_partitioning, count, &tcx.crate_name.as_str()); + merge_codegen_units(&mut initial_partitioning, count, &tcx.crate_name.as_str()); debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); } @@ -242,6 +244,8 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut post_inlining = place_inlined_translation_items(initial_partitioning, inlining_map); + post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(&tcx)); + debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter()); // Next we try to make as many symbols "internal" as possible, so LLVM has @@ -405,8 +409,7 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn merge_codegen_units<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - initial_partitioning: &mut PreInliningPartitioning<'tcx>, +fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<'tcx>, target_cgu_count: usize, crate_name: &str) { assert!(target_cgu_count >= 1); @@ -423,51 +426,16 @@ fn merge_codegen_units<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // the stable sort below will keep everything nice and deterministic. codegen_units.sort_by_key(|cgu| cgu.name().clone()); - // Estimate the size of a codegen unit as (approximately) the number of MIR - // statements it corresponds to. - fn codegen_unit_size_estimate<'a, 'tcx>(cgu: &CodegenUnit<'tcx>, - mono_item_sizes: &HashMap) - -> usize { - cgu.items().keys().map(|mi| mono_item_sizes.get(mi).unwrap()).sum() - } - - fn mono_item_size_estimate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - item: &MonoItem<'tcx>) - -> usize { - match item { - MonoItem::Fn(instance) => { - // Estimate the size of a function based on how many statements - // it contains. - let mir = tcx.instance_mir(instance.def); - mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() - }, - // Conservatively estimate the size of a static declaration - // or assembly to be 1. - MonoItem::Static(_) | MonoItem::GlobalAsm(_) => 1, - } - } - - // Since `sort_by_key` currently recomputes the keys for each comparison, - // we can save unnecessary recomputations by storing size estimates for - // each `MonoItem`. Storing estimates for `CodegenUnit` might be preferable, - // but its structure makes it awkward to use as a key and additionally their - // sizes change as the merging occurs, requiring the map to be updated. - let mut sizes: HashMap = HashMap::new(); - for mis in codegen_units.iter().map(|cgu| cgu.items().keys()) { - mis.for_each(|mi| { - sizes.entry(*mi).or_insert_with(|| mono_item_size_estimate(tcx, mi)); - }); - } - // Merge the two smallest codegen units until the target size is reached. // Note that "size" is estimated here rather inaccurately as the number of // translation items in a given unit. This could be improved on. while codegen_units.len() > target_cgu_count { // Sort small cgus to the back - codegen_units.sort_by_key(|cgu| usize::MAX - codegen_unit_size_estimate(cgu, &sizes)); + codegen_units.sort_by_key(|cgu| usize::MAX - cgu.size_estimate()); let mut smallest = codegen_units.pop().unwrap(); let second_smallest = codegen_units.last_mut().unwrap(); + second_smallest.modify_size_estimate(smallest.size_estimate()); for (k, v) in smallest.items_mut().drain() { second_smallest.items_mut().insert(k, v); } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 633ed9b32cd1e..e03b6ee794d67 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -79,7 +79,7 @@ use std::ffi::CString; use std::str; use std::sync::Arc; use std::time::{Instant, Duration}; -use std::i32; +use std::{i32, usize}; use std::iter; use std::sync::mpsc; use syntax_pos::Span; @@ -829,7 +829,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // account the size of each TransItem. let codegen_units = { let mut codegen_units = codegen_units; - codegen_units.sort_by_key(|cgu| -(cgu.items().len() as isize)); + codegen_units.sort_by_key(|cgu| usize::MAX - cgu.size_estimate()); codegen_units }; From c8e9da44a9e361b43548cb60a46cf3c2138968c7 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 15 Jan 2018 18:52:42 +0000 Subject: [PATCH 08/70] Update comments about the partitioning inefficiency --- src/librustc_mir/monomorphize/partitioning.rs | 2 -- src/librustc_trans/base.rs | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 4150f9f95485b..806d787c84522 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -427,8 +427,6 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< codegen_units.sort_by_key(|cgu| cgu.name().clone()); // Merge the two smallest codegen units until the target size is reached. - // Note that "size" is estimated here rather inaccurately as the number of - // translation items in a given unit. This could be improved on. while codegen_units.len() > target_cgu_count { // Sort small cgus to the back codegen_units.sort_by_key(|cgu| usize::MAX - cgu.size_estimate()); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index e03b6ee794d67..1126c9d2c8cdb 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -824,9 +824,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); // We sort the codegen units by size. This way we can schedule work for LLVM - // a bit more efficiently. Note that "size" is defined rather crudely at the - // moment as it is just the number of TransItems in the CGU, not taking into - // account the size of each TransItem. + // a bit more efficiently. let codegen_units = { let mut codegen_units = codegen_units; codegen_units.sort_by_key(|cgu| usize::MAX - cgu.size_estimate()); From 5c9a8b504146cc9f755c4e7d73b3693cb781021f Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 19 Jan 2018 00:32:58 +0000 Subject: [PATCH 09/70] Add instance_def_size_estimate query --- src/librustc/dep_graph/dep_node.rs | 1 + src/librustc/mir/mono.rs | 3 +-- src/librustc/ty/maps/config.rs | 6 ++++++ src/librustc/ty/maps/mod.rs | 10 ++++++++++ src/librustc/ty/maps/plumbing.rs | 1 + src/librustc/ty/mod.rs | 14 ++++++++++++++ 6 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 14f54fbffac2e..1de9091b5df7d 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -638,6 +638,7 @@ define_dep_nodes!( <'tcx> [input] TargetFeaturesWhitelist, [] TargetFeaturesEnabled(DefId), + [] InstanceDefSizeEstimate { instance_def: InstanceDef<'tcx> }, ); trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index bbef045a305a5..2af2219d26785 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -31,8 +31,7 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::Fn(instance) => { // Estimate the size of a function based on how many statements // it contains. - let mir = tcx.instance_mir(instance.def); - mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() + tcx.instance_def_size_estimate(instance.def) }, // Conservatively estimate the size of a static declaration // or assembly to be 1. diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 8dedcb24c2fb6..b0adfe2087966 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -637,6 +637,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::target_features_whitelist<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::instance_def_size_estimate<'tcx> { + fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { + format!("estimating size for `{}`", tcx.item_path_str(def.def_id())) + } +} + macro_rules! impl_disk_cacheable_query( ($query_name:ident, |$key:tt| $cond:expr) => { impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> { diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index e7e92b8a4288f..6c79f6a62fa0b 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -365,6 +365,9 @@ define_maps! { <'tcx> target_features_whitelist_node(CrateNum) -> Rc>, [] fn target_features_enabled: TargetFeaturesEnabled(DefId) -> Rc>, + // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. + [] fn instance_def_size_estimate: instance_def_size_estimate_dep_node(ty::InstanceDef<'tcx>) + -> usize, } ////////////////////////////////////////////////////////////////////// @@ -514,3 +517,10 @@ fn substitute_normalize_and_test_predicates_node<'tcx>(key: (DefId, &'tcx Substs fn target_features_whitelist_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::TargetFeaturesWhitelist } + +fn instance_def_size_estimate_dep_node<'tcx>(instance_def: ty::InstanceDef<'tcx>) + -> DepConstructor<'tcx> { + DepConstructor::InstanceDefSizeEstimate { + instance_def + } +} diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index d670ecc2691ae..f35693c702b32 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -761,6 +761,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::EraseRegionsTy | DepKind::NormalizeTy | DepKind::SubstituteNormalizeAndTestPredicates | + DepKind::InstanceDefSizeEstimate | // This one should never occur in this context DepKind::Null => { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 2b4d2c80c6f9e..1d64e7bae913c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2669,6 +2669,19 @@ fn crate_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx.hir.crate_hash } +fn instance_def_size_estimate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance_def: InstanceDef<'tcx>) + -> usize { + match instance_def { + InstanceDef::Item(def_id) => { + let mir = tcx.optimized_mir(def_id); + mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() + }, + // Estimate the size of compiler-generated shims to be 1. + _ => 1 + } +} + pub fn provide(providers: &mut ty::maps::Providers) { context::provide(providers); erase_regions::provide(providers); @@ -2686,6 +2699,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { original_crate_name, crate_hash, trait_impls_of: trait_def::trait_impls_of_provider, + instance_def_size_estimate, ..*providers }; } From 62703cfd26592be8ea641912aa61ecf5d1cbe64f Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 19 Jan 2018 11:51:48 +0000 Subject: [PATCH 10/70] Estimate size of InstanceDef::DropGlue more accurately Also a little bit of clean up. --- src/librustc/mir/mono.rs | 6 +++--- src/librustc/ty/mod.rs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index 2af2219d26785..49e5c0dc21f9e 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -157,8 +157,7 @@ impl<'tcx> CodegenUnit<'tcx> { pub fn size_estimate(&self) -> usize { // Should only be called if `estimate_size` has previously been called. - assert!(self.size_estimate.is_some()); - self.size_estimate.unwrap() + self.size_estimate.expect("estimate_size must be called before getting a size_estimate") } pub fn modify_size_estimate(&mut self, delta: usize) { @@ -176,7 +175,8 @@ impl<'tcx> HashStable> for CodegenUnit<'tcx> { let CodegenUnit { ref items, name, - .. + // The size estimate is not relevant to the hash + size_estimate: _, } = *self; name.hash_stable(hcx, hasher); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 1d64e7bae913c..332afe36010bd 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2673,11 +2673,12 @@ fn instance_def_size_estimate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance_def: InstanceDef<'tcx>) -> usize { match instance_def { - InstanceDef::Item(def_id) => { - let mir = tcx.optimized_mir(def_id); + InstanceDef::Item(..) | + InstanceDef::DropGlue(..) => { + let mir = tcx.instance_mir(instance_def); mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() }, - // Estimate the size of compiler-generated shims to be 1. + // Estimate the size of other compiler-generated shims to be 1. _ => 1 } } From 768cbbcd9e21c75565dad4714346b0d0028d331c Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 19 Jan 2018 19:58:46 +0000 Subject: [PATCH 11/70] Fix type inhabitedness check for arrays Arrays of uninhabited types were considered to also be uninhabited if their length had not been evaluated, causing unsoundness. --- src/librustc/ty/inhabitedness/mod.rs | 9 ++++---- src/test/compile-fail/empty-never-array.rs | 26 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 src/test/compile-fail/empty-never-array.rs diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 0072512464a0e..8cf5b431cf12d 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -262,10 +262,11 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { })) }, TyArray(ty, len) => { - if len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) { - DefIdForest::empty() - } else { - ty.uninhabited_from(visited, tcx) + match len.val.to_const_int().and_then(|i| i.to_u64()) { + Some(n) if n != 0 => ty.uninhabited_from(visited, tcx), + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + _ => DefIdForest::empty() } } TyRef(_, ref tm) => { diff --git a/src/test/compile-fail/empty-never-array.rs b/src/test/compile-fail/empty-never-array.rs new file mode 100644 index 0000000000000..1dc45a1a88458 --- /dev/null +++ b/src/test/compile-fail/empty-never-array.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +enum Helper { + T(T, [!; 0]), + #[allow(dead_code)] + U(U), +} + +fn transmute(t: T) -> U { + let Helper::U(u) = Helper::T(t, []); //~ ERROR refutable pattern in local binding: `T(_, _)` not covered + u +} + +fn main() { + println!("{:?}", transmute::<&str, (*const u8, u64)>("type safety")); +} From c4d0bb398b548dafba2346f3f3be9284909643f0 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 19 Jan 2018 21:00:35 +0000 Subject: [PATCH 12/70] Fix tidy error --- src/librustc/ty/inhabitedness/mod.rs | 2 +- src/test/compile-fail/empty-never-array.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 8cf5b431cf12d..93e4cd9adf888 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -263,9 +263,9 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { }, TyArray(ty, len) => { match len.val.to_const_int().and_then(|i| i.to_u64()) { - Some(n) if n != 0 => ty.uninhabited_from(visited, tcx), // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. + Some(n) if n != 0 => ty.uninhabited_from(visited, tcx), _ => DefIdForest::empty() } } diff --git a/src/test/compile-fail/empty-never-array.rs b/src/test/compile-fail/empty-never-array.rs index 1dc45a1a88458..53b24e1731932 100644 --- a/src/test/compile-fail/empty-never-array.rs +++ b/src/test/compile-fail/empty-never-array.rs @@ -17,7 +17,8 @@ enum Helper { } fn transmute(t: T) -> U { - let Helper::U(u) = Helper::T(t, []); //~ ERROR refutable pattern in local binding: `T(_, _)` not covered + let Helper::U(u) = Helper::T(t, []); + //~^ ERROR refutable pattern in local binding: `T(_, _)` not covered u } From 7188706c4fbbae660fa7eb6f2bf13130ddf1726a Mon Sep 17 00:00:00 2001 From: "A.J. Gardner" Date: Sat, 20 Jan 2018 14:32:33 -0600 Subject: [PATCH 13/70] Teach rustc about DW_AT_noreturn and a few more DIFlags --- src/librustc_llvm/ffi.rs | 4 ++++ src/librustc_trans/debuginfo/mod.rs | 3 +++ src/rustllvm/RustWrapper.cpp | 20 ++++++++++++++++++-- src/test/codegen/noreturnflag.rs | 29 +++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/test/codegen/noreturnflag.rs diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index b97e37f4c8fb9..8602c559da9b6 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -498,6 +498,10 @@ pub mod debuginfo { const FlagStaticMember = (1 << 12); const FlagLValueReference = (1 << 13); const FlagRValueReference = (1 << 14); + const FlagExternalTypeRef = (1 << 15); + const FlagIntroducedVirtual = (1 << 18); + const FlagBitField = (1 << 19); + const FlagNoReturn = (1 << 20); const FlagMainSubprogram = (1 << 21); } } diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index b46e12d9d5b67..9071eb776d529 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -270,6 +270,9 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, } None => {} }; + if sig.output().is_never() { + flags = flags | DIFlags::FlagNoReturn; + } let fn_metadata = unsafe { llvm::LLVMRustDIBuilderCreateFunction( diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 95130d596e165..2e8207d1cbbd9 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -457,9 +457,13 @@ enum class LLVMRustDIFlags : uint32_t { FlagStaticMember = (1 << 12), FlagLValueReference = (1 << 13), FlagRValueReference = (1 << 14), - FlagMainSubprogram = (1 << 21), + FlagExternalTypeRef = (1 << 15), + FlagIntroducedVirtual = (1 << 18), + FlagBitField = (1 << 19), + FlagNoReturn = (1 << 20), + FlagMainSubprogram = (1 << 21), // Do not add values that are not supported by the minimum LLVM - // version we support! + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def }; inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { @@ -545,6 +549,18 @@ static unsigned fromRust(LLVMRustDIFlags Flags) { Result |= DINode::DIFlags::FlagRValueReference; } #if LLVM_RUSTLLVM || LLVM_VERSION_GE(4, 0) + if (isSet(Flags & LLVMRustDIFlags::FlagExternalTypeRef)) { + Result |= DINode::DIFlags::FlagExternalTypeRef; + } + if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { + Result |= DINode::DIFlags::FlagIntroducedVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { + Result |= DINode::DIFlags::FlagBitField; + } + if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { + Result |= DINode::DIFlags::FlagNoReturn; + } if (isSet(Flags & LLVMRustDIFlags::FlagMainSubprogram)) { Result |= DINode::DIFlags::FlagMainSubprogram; } diff --git a/src/test/codegen/noreturnflag.rs b/src/test/codegen/noreturnflag.rs new file mode 100644 index 0000000000000..473fed8e046f7 --- /dev/null +++ b/src/test/codegen/noreturnflag.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// min-llvm-version 3.8 + +// compile-flags: -g -C no-prepopulate-passes + +// CHECK-LABEL: foo +// CHECK: {{.*}}DISubprogram{{.*}} name: "foo",{{.*}}DIFlagNoReturn{{.*}} + +#[no_mangle] +pub fn foo() -> ! { + loop {} +} + +// CHECK-LABEL: main +// CHECK: {{.*}}DISubprogram{{.*}}name: "main",{{.*}}DIFlagMainSubprogram{{.*}} + +pub fn main() { + foo(); +} From 51fe2fe07fc81e177fe9b822bc4db91e51837e45 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sat, 20 Jan 2018 20:50:38 +0200 Subject: [PATCH 14/70] rustc_trans: remove an unwrap by replacing a bool with Result. --- src/librustc_trans/cabi_x86_64.rs | 56 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index 6db18bfecf258..caeb1a9e2c362 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -194,44 +194,48 @@ pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tc let mut sse_regs = 8; // XMM0-7 let mut x86_64_ty = |arg: &mut ArgType<'tcx>, is_arg: bool| { - let cls = classify_arg(cx, arg); + let mut cls_or_mem = classify_arg(cx, arg); let mut needed_int = 0; let mut needed_sse = 0; - let in_mem = match cls { - Err(Memory) => true, - Ok(ref cls) if is_arg => { - for &c in cls { + if is_arg { + if let Ok(cls) = cls_or_mem { + for &c in &cls { match c { Class::Int => needed_int += 1, Class::Sse => needed_sse += 1, _ => {} } } - arg.layout.is_aggregate() && - (int_regs < needed_int || sse_regs < needed_sse) + if arg.layout.is_aggregate() { + if int_regs < needed_int || sse_regs < needed_sse { + cls_or_mem = Err(Memory); + } + } } - Ok(_) => false - }; + } - if in_mem { - if is_arg { - arg.make_indirect_byval(); - } else { - // `sret` parameter thus one less integer register available - arg.make_indirect(); - int_regs -= 1; + match cls_or_mem { + Err(Memory) => { + if is_arg { + arg.make_indirect_byval(); + } else { + // `sret` parameter thus one less integer register available + arg.make_indirect(); + int_regs -= 1; + } } - } else { - // split into sized chunks passed individually - int_regs -= needed_int; - sse_regs -= needed_sse; - - if arg.layout.is_aggregate() { - let size = arg.layout.size; - arg.cast_to(cast_target(cls.as_ref().unwrap(), size)) - } else { - arg.extend_integer_width_to(32); + Ok(ref cls) => { + // split into sized chunks passed individually + int_regs -= needed_int; + sse_regs -= needed_sse; + + if arg.layout.is_aggregate() { + let size = arg.layout.size; + arg.cast_to(cast_target(cls, size)) + } else { + arg.extend_integer_width_to(32); + } } } }; From f7f6598083f24191bbdb170b993dbf97abd1b5c9 Mon Sep 17 00:00:00 2001 From: "A.J. Gardner" Date: Sat, 20 Jan 2018 21:22:11 -0600 Subject: [PATCH 15/70] Simplify and fix test --- src/test/codegen/noreturnflag.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/test/codegen/noreturnflag.rs b/src/test/codegen/noreturnflag.rs index 473fed8e046f7..3fa7921b4448d 100644 --- a/src/test/codegen/noreturnflag.rs +++ b/src/test/codegen/noreturnflag.rs @@ -13,15 +13,12 @@ // compile-flags: -g -C no-prepopulate-passes -// CHECK-LABEL: foo -// CHECK: {{.*}}DISubprogram{{.*}} name: "foo",{{.*}}DIFlagNoReturn{{.*}} +// CHECK: {{.*}}DISubprogram{{.*}}name: "foo"{{.*}}DIFlagNoReturn -#[no_mangle] -pub fn foo() -> ! { +fn foo() -> ! { loop {} } -// CHECK-LABEL: main // CHECK: {{.*}}DISubprogram{{.*}}name: "main",{{.*}}DIFlagMainSubprogram{{.*}} pub fn main() { From e0f9b26899ea16bb2b6b966266e46698ebad9c4a Mon Sep 17 00:00:00 2001 From: "A.J. Gardner" Date: Sun, 21 Jan 2018 12:36:25 -0600 Subject: [PATCH 16/70] Ensure test doesn't run with llvm 3.9 --- src/rustllvm/RustWrapper.cpp | 2 +- src/test/codegen/noreturnflag.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 2e8207d1cbbd9..42ddf5663f9ad 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -548,7 +548,6 @@ static unsigned fromRust(LLVMRustDIFlags Flags) { if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { Result |= DINode::DIFlags::FlagRValueReference; } -#if LLVM_RUSTLLVM || LLVM_VERSION_GE(4, 0) if (isSet(Flags & LLVMRustDIFlags::FlagExternalTypeRef)) { Result |= DINode::DIFlags::FlagExternalTypeRef; } @@ -558,6 +557,7 @@ static unsigned fromRust(LLVMRustDIFlags Flags) { if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { Result |= DINode::DIFlags::FlagBitField; } +#if LLVM_RUSTLLVM || LLVM_VERSION_GE(4, 0) if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { Result |= DINode::DIFlags::FlagNoReturn; } diff --git a/src/test/codegen/noreturnflag.rs b/src/test/codegen/noreturnflag.rs index 3fa7921b4448d..24a5a4e44cb29 100644 --- a/src/test/codegen/noreturnflag.rs +++ b/src/test/codegen/noreturnflag.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// min-llvm-version 3.8 +// min-llvm-version 4.0 // compile-flags: -g -C no-prepopulate-passes @@ -19,8 +19,6 @@ fn foo() -> ! { loop {} } -// CHECK: {{.*}}DISubprogram{{.*}}name: "main",{{.*}}DIFlagMainSubprogram{{.*}} - pub fn main() { foo(); } From 0bbc4221dc897ee4048e66b87adb31fbbd6f619c Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Mon, 22 Jan 2018 19:58:13 +0900 Subject: [PATCH 17/70] Use std based dedup in projection Unstable sort was added recently, and the code that is being modified is 3 years old. As quicksort doesn't allocate it will likely perform as well as, or better than linear search. --- src/librustc/traits/project.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 3342d13dd6e5f..355216f8c1a18 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -824,21 +824,12 @@ fn project_type<'cx, 'gcx, 'tcx>( // Drop duplicates. // // Note: `candidates.vec` seems to be on the critical path of the - // compiler. Replacing it with an hash set was also tried, which would - // render the following dedup unnecessary. It led to cleaner code but - // prolonged compiling time of `librustc` from 5m30s to 6m in one test, or - // ~9% performance lost. - if candidates.vec.len() > 1 { - let mut i = 0; - while i < candidates.vec.len() { - let has_dup = (0..i).any(|j| candidates.vec[i] == candidates.vec[j]); - if has_dup { - candidates.vec.swap_remove(i); - } else { - i += 1; - } - } - } + // compiler. Replacing it with an HashSet was also tried, which would + // render the following dedup unnecessary. The original comment indicated + // that it was 9% slower, but that data is now obsolete and a new + // benchmark should be performed. + candidates.vec.sort_unstable(); + candidates.vec.dedup(); // Prefer where-clauses. As in select, if there are multiple // candidates, we prefer where-clause candidates over impls. This From 2d56abfbebdc905dafc9cf9edc0a6f58e4de7cbd Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 16 Jan 2018 01:44:32 +0300 Subject: [PATCH 18/70] AST/HIR: Add a separate structure for labels --- src/librustc/hir/intravisit.rs | 52 ++++++++++---------- src/librustc/hir/lowering.rs | 48 +++++++++---------- src/librustc/hir/mod.rs | 20 ++++++-- src/librustc/hir/print.rs | 24 +++++----- src/librustc/ich/impls_hir.rs | 7 ++- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc_passes/ast_validation.rs | 13 ++--- src/librustc_resolve/lib.rs | 14 +++--- src/libsyntax/ast.rs | 24 +++++++--- src/libsyntax/fold.rs | 43 +++++++++-------- src/libsyntax/parse/parser.rs | 64 +++++++++---------------- src/libsyntax/print/pprust.rs | 36 +++++++------- src/libsyntax/visit.rs | 58 ++++++++++------------ 13 files changed, 203 insertions(+), 202 deletions(-) diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index ce35e6552ca83..97cf9b01410b1 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -43,7 +43,6 @@ use syntax::abi::Abi; use syntax::ast::{NodeId, CRATE_NODE_ID, Name, Attribute}; -use syntax::codemap::Spanned; use syntax_pos::Span; use hir::*; use hir::def::Def; @@ -336,6 +335,9 @@ pub trait Visitor<'v> : Sized { fn visit_variant(&mut self, v: &'v Variant, g: &'v Generics, item_id: NodeId) { walk_variant(self, v, g, item_id) } + fn visit_label(&mut self, label: &'v Label) { + walk_label(self, label) + } fn visit_lifetime(&mut self, lifetime: &'v Lifetime) { walk_lifetime(self, lifetime) } @@ -370,18 +372,6 @@ pub trait Visitor<'v> : Sized { } } -pub fn walk_opt_name<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, opt_name: Option) { - if let Some(name) = opt_name { - visitor.visit_name(span, name); - } -} - -pub fn walk_opt_sp_name<'v, V: Visitor<'v>>(visitor: &mut V, opt_sp_name: &Option>) { - if let Some(ref sp_name) = *opt_sp_name { - visitor.visit_name(sp_name.span, sp_name.node); - } -} - /// Walks the contents of a crate. See also `Crate::visit_all_items`. pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate) { visitor.visit_mod(&krate.module, krate.span, CRATE_NODE_ID); @@ -420,6 +410,10 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) { walk_list!(visitor, visit_ty, &local.ty); } +pub fn walk_label<'v, V: Visitor<'v>>(visitor: &mut V, label: &'v Label) { + visitor.visit_name(label.span, label.name); +} + pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) { visitor.visit_id(lifetime.id); match lifetime.name { @@ -452,7 +446,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { match item.node { ItemExternCrate(opt_name) => { visitor.visit_id(item.id); - walk_opt_name(visitor, item.span, opt_name) + if let Some(name) = opt_name { + visitor.visit_name(item.span, name); + } } ItemUse(ref path, _) => { visitor.visit_id(item.id); @@ -993,14 +989,14 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(if_block); walk_list!(visitor, visit_expr, optional_else); } - ExprWhile(ref subexpression, ref block, ref opt_sp_name) => { + ExprWhile(ref subexpression, ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); visitor.visit_expr(subexpression); visitor.visit_block(block); - walk_opt_sp_name(visitor, opt_sp_name); } - ExprLoop(ref block, ref opt_sp_name, _) => { + ExprLoop(ref block, ref opt_label, _) => { + walk_list!(visitor, visit_label, opt_label); visitor.visit_block(block); - walk_opt_sp_name(visitor, opt_sp_name); } ExprMatch(ref subexpression, ref arms, _) => { visitor.visit_expr(subexpression); @@ -1036,28 +1032,28 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { ExprPath(ref qpath) => { visitor.visit_qpath(qpath, expression.id, expression.span); } - ExprBreak(label, ref opt_expr) => { - label.ident.map(|ident| { - match label.target_id { + ExprBreak(ref destination, ref opt_expr) => { + if let Some(ref label) = destination.label { + visitor.visit_label(label); + match destination.target_id { ScopeTarget::Block(node_id) | ScopeTarget::Loop(LoopIdResult::Ok(node_id)) => visitor.visit_def_mention(Def::Label(node_id)), ScopeTarget::Loop(LoopIdResult::Err(_)) => {}, }; - visitor.visit_name(ident.span, ident.node.name); - }); + } walk_list!(visitor, visit_expr, opt_expr); } - ExprAgain(label) => { - label.ident.map(|ident| { - match label.target_id { + ExprAgain(ref destination) => { + if let Some(ref label) = destination.label { + visitor.visit_label(label); + match destination.target_id { ScopeTarget::Block(_) => bug!("can't `continue` to a non-loop block"), ScopeTarget::Loop(LoopIdResult::Ok(node_id)) => visitor.visit_def_mention(Def::Label(node_id)), ScopeTarget::Loop(LoopIdResult::Err(_)) => {}, }; - visitor.visit_name(ident.span, ident.node.name); - }); + } } ExprRet(ref optional_expression) => { walk_list!(visitor, visit_expr, optional_expression); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 32b55a05124ac..adfcf42d85b94 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -768,22 +768,22 @@ impl<'a> LoweringContext<'a> { *self.name_map.entry(ident).or_insert_with(|| Symbol::from_ident(ident)) } - fn lower_opt_sp_ident(&mut self, o_id: Option>) -> Option> { - o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name)) + fn lower_label(&mut self, label: Option