From 51bb1da2fbd0a57c67e86526ac430d7e6206bcf8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Nov 2019 16:04:40 +0100 Subject: [PATCH 01/13] save LTO import information and check it when trying to reuse build products. resolves issue 59535. --- src/librustc_codegen_llvm/back/lto.rs | 125 ++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 858dd59b26148..4d2973b7f753e 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -19,10 +19,19 @@ use rustc_data_structures::fx::FxHashMap; use rustc_codegen_ssa::{RLIB_BYTECODE_EXTENSION, ModuleCodegen, ModuleKind}; use std::ffi::{CStr, CString}; +use std::fs::File; +use std::io; +use std::mem; +use std::path::Path; use std::ptr; use std::slice; use std::sync::Arc; +/// We keep track of past LTO imports that were used to produce the current set +/// of compiled object files that we might choose to reuse during this +/// compilation session. +pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-imports.bin"; + pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { match crate_type { config::CrateType::Executable | @@ -470,13 +479,26 @@ fn thin_lto(cgcx: &CodegenContext, info!("thin LTO data created"); - let import_map = if cgcx.incr_comp_session_dir.is_some() { - ThinLTOImports::from_thin_lto_data(data) + let (import_map_path, prev_import_map, mut curr_import_map) = + if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir + { + let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME); + let prev = if path.exists() { + // FIXME: can/should we recover from IO error occuring here + // (e.g. by clearing green_modules), rather than panicking from + // unwrap call? + Some(ThinLTOImports::load_from_file(&path).unwrap()) + } else { + None + }; + let curr = ThinLTOImports::from_thin_lto_data(data); + (Some(path), prev, curr) } else { // If we don't compile incrementally, we don't need to load the // import data from LLVM. assert!(green_modules.is_empty()); - ThinLTOImports::default() + let curr = ThinLTOImports::default(); + (None, None, curr) }; info!("thin LTO import map loaded"); @@ -500,11 +522,24 @@ fn thin_lto(cgcx: &CodegenContext, for (module_index, module_name) in shared.module_names.iter().enumerate() { let module_name = module_name_to_str(module_name); - // If the module hasn't changed and none of the modules it imports - // from has changed, we can re-use the post-ThinLTO version of the - // module. + // If the module hasn't changed, and none of the modules it imported + // from (*) has changed, then we can (1.) re-use the post-ThinLTO + // version of the module, and thus (2.) save those previous ThinLTO + // imports as its current set of imports. + // + // (*): that is, "imported from" at the time it was previously ThinLTO'ed; + // see rust-lang/rust#59535. + // + // If we do *not* re-use the post-ThinLTO version of the module, + // then we should save the computed imports that LLVM reported to us + // during this cycle. if green_modules.contains_key(module_name) { - let imports_all_green = import_map.modules_imported_by(module_name) + assert!(cgcx.incr_comp_session_dir.is_some()); + assert!(prev_import_map.is_some()); + + let prev_import_map = prev_import_map.as_ref().unwrap(); + let prev_imports = prev_import_map.modules_imported_by(module_name); + let imports_all_green = prev_imports .iter() .all(|imported_module| green_modules.contains_key(imported_module)); @@ -512,8 +547,10 @@ fn thin_lto(cgcx: &CodegenContext, let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); + assert!(cgcx.incr_comp_session_dir.is_some()); cgcx.cgu_reuse_tracker.set_actual_reuse(module_name, CguReuse::PostLto); + curr_import_map.overwrite_with_past_import_state(module_name, prev_imports); continue } } @@ -525,6 +562,14 @@ fn thin_lto(cgcx: &CodegenContext, })); } + // Save the accumulated ThinLTO import information + if let Some(path) = import_map_path { + if let Err(err) = curr_import_map.save_to_file(&path) { + let msg = format!("Error while writing ThinLTO import data: {}", err); + return Err(write::llvm_err(&diag_handler, &msg)); + } + } + Ok((opt_jobs, copy_jobs)) } } @@ -826,10 +871,76 @@ pub struct ThinLTOImports { } impl ThinLTOImports { + /// Records `llvm_module_name` as importing `prev_imports` rather than what + /// we have currently computed. Ensures that the previous imports are a + /// superset of what imports LLVM currently computed. + fn overwrite_with_past_import_state(&mut self, + llvm_module_name: &str, + prev_imports: &[String]) { + // There were imports used to previous LTO optimize the module; make + // sure they are a superset of whatever we have in our current state, + // and then store them as our new current state. + let curr_imports_for_mod = self.modules_imported_by(llvm_module_name); + for imported in curr_imports_for_mod { + assert!(prev_imports.contains(imported)); + } + // We can avoid doing the insertion entirely if the sets are equivalent, + // and we can determine equivalence cheaply via a length comparison, + // since we have already asserted the past state to be a superset of the + // current state. + if prev_imports.len() != curr_imports_for_mod.len() { + debug!("ThinLTOImports::restore_past_import_state \ + mod: {:?} replacing curr state: {:?} with prev state: {:?}", + llvm_module_name, curr_imports_for_mod, prev_imports); + self.imports.insert(llvm_module_name.to_owned(), prev_imports.to_vec()); + } + } + fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] { self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) } + fn save_to_file(&self, path: &Path) -> io::Result<()> { + use std::io::Write; + let file = File::create(path)?; + let mut writer = io::BufWriter::new(file); + for (importing_module_name, imported_modules) in &self.imports { + writeln!(writer, "{}", importing_module_name)?; + for imported_module in imported_modules { + writeln!(writer, " {}", imported_module)?; + } + writeln!(writer)?; + } + Ok(()) + } + + fn load_from_file(path: &Path) -> io::Result { + use std::io::BufRead; + let mut imports = FxHashMap::default(); + let mut current_module = None; + let mut current_imports = vec![]; + let file = File::open(path)?; + for line in io::BufReader::new(file).lines() { + let line = line?; + if line.is_empty() { + let importing_module = current_module + .take() + .expect("Importing module not set"); + imports.insert(importing_module, + mem::replace(&mut current_imports, vec![])); + } else if line.starts_with(" ") { + // Space marks an imported module + assert_ne!(current_module, None); + current_imports.push(line.trim().to_string()); + } else { + // Otherwise, beginning of a new module (must be start or follow empty line) + assert_eq!(current_module, None); + current_module = Some(line.trim().to_string()); + } + } + Ok(ThinLTOImports { imports }) + } + /// Loads the ThinLTO import map from ThinLTOData. unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports { unsafe extern "C" fn imported_module_callback(payload: *mut libc::c_void, From e3aac4a8b5b778a59b1f62c1f08bf8fbcafa372f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 4 Dec 2019 13:53:49 +0100 Subject: [PATCH 02/13] regression test for issue. --- .../Makefile | 69 + .../cortex-m-rt.rs | 13 + .../device.x | 3 + .../link.x.in | 213 +++ .../memory.x | 23 + .../nrf52810-hal.rs | 1643 +++++++++++++++++ .../rubble.rs.v1 | 61 + .../rubble.rs.v2 | 65 + 8 files changed, 2090 insertions(+) create mode 100644 src/test/run-make/removing-code-and-incremental-lto/Makefile create mode 100644 src/test/run-make/removing-code-and-incremental-lto/cortex-m-rt.rs create mode 100644 src/test/run-make/removing-code-and-incremental-lto/device.x create mode 100644 src/test/run-make/removing-code-and-incremental-lto/link.x.in create mode 100644 src/test/run-make/removing-code-and-incremental-lto/memory.x create mode 100644 src/test/run-make/removing-code-and-incremental-lto/nrf52810-hal.rs create mode 100644 src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v1 create mode 100644 src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v2 diff --git a/src/test/run-make/removing-code-and-incremental-lto/Makefile b/src/test/run-make/removing-code-and-incremental-lto/Makefile new file mode 100644 index 0000000000000..5b9387e6e8a05 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/Makefile @@ -0,0 +1,69 @@ +-include ../../run-make-fulldeps/tools.mk + +# How to run this +# $ ./x.py clean +# $ ./x.py test --target thumbv7m-none-eabi src/test/run-make + +# The original target of interest was thumbv7em-none-eabi + +# This is a test of a scenario that arose in issue rust-lang/rust#59535. +# +# What the user experienced: deleting a method use caused a link-time failure. +# +# What the cause was: At the time, incremental compilation would determine which +# object files could be reused from a previous build based on, in part, 1. the +# "greenness" of the modules in the current crate (i.e. which modules' contents +# have changed since the incremental build) and 2. the current LTO import +# information of the modules in the current crate. +# +# The problem was that the reused object file could have been optimized based on +# LTO imports of a *previous* compile, not the current one. In other words, the +# past LTO import information could have included more modules than what the +# current LTO imports do, and those dependencies need to be respected when you +# decide what object files to reuse. +# +# To make this more concrete: Here is the the high-level description of the +# specific scenario from rust-lang/rust#59535: +# +# We had a call-graph like this: `[A] -> [B -> D] <- [C]`, where modules are in `[]` +# +# and the change between incremental builds was the `D <- C` link was removed. +# +# 1ST COMPILE: At the time of the first compile, LTO-optimization inlined the +# code from `B` into `A`, so that in the object code for `A`, there was now a +# direct link to the symbol for `D`. The LTO imports computed during this first +# compile showed this directly: it said that `[A]` imported `B` and `D`. +# +# 2ND COMPIILE: But on the second compile, after the developer removed the call +# `D <- C`, the Rust compiler itself determined that the `D` could be declared +# as an internal definition, and then LLVM optimized the definition of `D` away +# entirely. At the *same time*, the LTO imports computed during this second +# compile reported by LLVM said that `[A]` *solely* imported `B`, and so the +# Rust compiler's incremental reuse mechanism determined that we could reuse the +# object code previously generated (during the 1st compile) for `[A]`. This +# conclusion seemed valid to the compiler, because nothing changed about any +# imports that it knew of for `[A]` based on the current compile. + +### (Switch to rustup's nightly to observe bug) +# RUSTC := rustc +nightly +RUSTC := $(RUSTC_ORIGINAL) + +OUT_DIR = $(TMPDIR) + +INCREMENTAL_DIR = $(OUT_DIR)/incr +RUBBLE1_OUT_DIR = $(OUT_DIR)/rubble1 +RUBBLE2_OUT_DIR = $(OUT_DIR)/rubble2 + +LINK_X_DIR := . + +all: + mkdir -p $(OUT_DIR) + mkdir -p $(INCREMENTAL_DIR) + mkdir -p $(RUBBLE1_OUT_DIR) + mkdir -p $(RUBBLE2_OUT_DIR) + $(RUSTC) --crate-name cortex_m_rt cortex-m-rt.rs --crate-type lib --emit=metadata,link -C opt-level=s --out-dir $(OUT_DIR) --target $(TARGET) -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) + $(RUSTC) --edition=2018 --crate-name nrf52810_hal nrf52810-hal.rs --crate-type lib --emit=metadata,link -C opt-level=s -C metadata=aa86958b67bf89f5 --out-dir $(OUT_DIR) --target $(TARGET) --extern cortex_m_rt=$(OUT_DIR)/libcortex_m_rt.rmeta -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) + cp rubble.rs.v1 $(TMPDIR)/rubble.rs + $(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE1_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2 + cp rubble.rs.v2 $(TMPDIR)/rubble.rs + $(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE2_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2 diff --git a/src/test/run-make/removing-code-and-incremental-lto/cortex-m-rt.rs b/src/test/run-make/removing-code-and-incremental-lto/cortex-m-rt.rs new file mode 100644 index 0000000000000..1ecc41ea60ee8 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/cortex-m-rt.rs @@ -0,0 +1,13 @@ +#![no_std] + +#[link_section = ".vector_table.reset_vector"] +#[no_mangle] pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; + +extern "Rust" { fn main() -> !; } + +#[no_mangle] pub unsafe extern "C" fn Reset() -> ! { main() } + +#[no_mangle] pub unsafe extern "C" fn DefaultHandler_() -> ! { loop { } } + +#[link_section = ".vector_table.exceptions"] +#[no_mangle] pub static __EXCEPTIONS: [usize; 14] = [0; 14]; diff --git a/src/test/run-make/removing-code-and-incremental-lto/device.x b/src/test/run-make/removing-code-and-incremental-lto/device.x new file mode 100644 index 0000000000000..28f975e0790d2 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/device.x @@ -0,0 +1,3 @@ +/* Sample device.x file */ +PROVIDE(WWDG = DefaultHandler); +PROVIDE(PVD = DefaultHandler); diff --git a/src/test/run-make/removing-code-and-incremental-lto/link.x.in b/src/test/run-make/removing-code-and-incremental-lto/link.x.in new file mode 100644 index 0000000000000..30d4af101f231 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/link.x.in @@ -0,0 +1,213 @@ +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut __sbss }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization + routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see + "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +/* Provides information about the memory layout of the device */ +/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */ +INCLUDE memory.x + +/* # Entry point = reset vector */ +ENTRY(Reset); +EXTERN(__RESET_VECTOR); /* depends on the `Reset` symbol */ + +/* # Exception vectors */ +/* This is effectively weak aliasing at the linker level */ +/* The user can override any of these aliases by defining the corresponding symbol themselves (cf. + the `exception!` macro) */ +EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ + +EXTERN(DefaultHandler); + +PROVIDE(NonMaskableInt = DefaultHandler); +EXTERN(HardFaultTrampoline); +PROVIDE(MemoryManagement = DefaultHandler); +PROVIDE(BusFault = DefaultHandler); +PROVIDE(UsageFault = DefaultHandler); +PROVIDE(SecureFault = DefaultHandler); +PROVIDE(SVCall = DefaultHandler); +PROVIDE(DebugMonitor = DefaultHandler); +PROVIDE(PendSV = DefaultHandler); +PROVIDE(SysTick = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultHandler_); +PROVIDE(HardFault = HardFault_); + +/* # Interrupt vectors */ +EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ + +/* # Pre-initialization function */ +/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = DefaultPreInit); + +/* # Sections */ +SECTIONS +{ + PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); + + /* ## Sections in FLASH */ + /* ### Vector table */ + .vector_table ORIGIN(FLASH) : + { + /* Initial Stack Pointer (SP) value */ + LONG(_stack_start); + + /* Reset vector */ + KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */ + __reset_vector = .; + + /* Exceptions */ + KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */ + __eexceptions = .; + + /* Device specific interrupts */ + KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ + } > FLASH + + PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); + + /* ### .text */ + .text _stext : + { + *(.text .text.*); + *(.HardFaultTrampoline); + *(.HardFault.*); + . = ALIGN(4); + __etext = .; + } > FLASH + + /* ### .rodata */ + .rodata __etext : ALIGN(4) + { + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + __erodata = .; + } > FLASH + + /* ## Sections in RAM */ + /* ### .data */ + .data : AT(__erodata) ALIGN(4) + { + . = ALIGN(4); + __sdata = .; + *(.data .data.*); + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + __edata = .; + } > RAM + + /* LMA of .data */ + __sidata = LOADADDR(.data); + + /* ### .bss */ + .bss : ALIGN(4) + { + . = ALIGN(4); + __sbss = .; + *(.bss .bss.*); + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + __ebss = .; + } > RAM + + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + *(.uninit .uninit.*); + . = ALIGN(4); + } > RAM + + /* Place the heap right after `.uninit` */ + . = ALIGN(4); + __sheap = .; + + /* ## .got */ + /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in + the input files and raise an error if relocatable code is found */ + .got (NOLOAD) : + { + KEEP(*(.got .got.*)); + } + + /* ## Discarded sections */ + /DISCARD/ : + { + /* Unused exception related info that only wastes space */ + *(.ARM.exidx); + *(.ARM.exidx.*); + *(.ARM.extab.*); + } +} + +/* Do not exceed this mark in the error messages below | */ +/* # Alignment checks */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(cortex-m-rt): the start of the FLASH region must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 4 == 0, " +ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); + +ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " +BUG(cortex-m-rt): .data is not 4-byte aligned"); + +ASSERT(__sidata % 4 == 0, " +BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " +BUG(cortex-m-rt): .bss is not 4-byte aligned"); + +ASSERT(__sheap % 4 == 0, " +BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); + +/* # Position checks */ + +/* ## .vector_table */ +ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, " +BUG(cortex-m-rt): the reset vector is missing"); + +ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, " +BUG(cortex-m-rt): the exception vectors are missing"); + +ASSERT(SIZEOF(.vector_table) > 0x40, " +ERROR(cortex-m-rt): The interrupt vectors are missing. +Possible solutions, from most likely to less likely: +- Link to a svd2rust generated device crate +- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency +may be enabling it) +- Supply the interrupt handlers yourself. Check the documentation for details."); + +/* ## .text */ +ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " +ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section +Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(cortex-m-rt): The .text section must be placed inside the FLASH memory. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +/* # Other checks */ +ASSERT(SIZEOF(.got) == 0, " +ERROR(cortex-m-rt): .got section detected in the input object files +Dynamic relocations are not supported. If you are linking to C code compiled using +the 'cc' crate then modify your build script to compile the C code _without_ +the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); +/* Do not exceed this mark in the error messages above | */ diff --git a/src/test/run-make/removing-code-and-incremental-lto/memory.x b/src/test/run-make/removing-code-and-incremental-lto/memory.x new file mode 100644 index 0000000000000..1be105b582716 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/memory.x @@ -0,0 +1,23 @@ +/* Linker script for the nRF52 - WITHOUT SOFT DEVICE */ +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 192K + RAM : ORIGIN = 0x20000000, LENGTH = 24K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Size of the heap (in bytes) */ +/* _heap_size = 1024; */ diff --git a/src/test/run-make/removing-code-and-incremental-lto/nrf52810-hal.rs b/src/test/run-make/removing-code-and-incremental-lto/nrf52810-hal.rs new file mode 100644 index 0000000000000..cae8f7192df21 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/nrf52810-hal.rs @@ -0,0 +1,1643 @@ +#![allow(non_camel_case_types, dead_code)] +#![no_std] + +pub mod nrf52810_pac { + extern crate cortex_m_rt; + pub struct Vector { _handler: unsafe extern "C" fn(), } + extern "C" fn power_clock_2() { } + + #[link_section = ".vector_table.interrupts"] + #[no_mangle] + pub static __INTERRUPTS: [Vector; 1] = [ Vector { _handler: power_clock_2 } ]; + + mod ficr { + mod info { + mod part { + #[derive(Debug, PartialEq)]struct PARTR; + struct R; + impl R { } + impl R { } + impl R { } + } + mod package { + #[derive(Debug, PartialEq)]struct PACKAGER; + struct R; + impl R { } + impl R { } + impl R { } + } + mod flash { + #[derive(Debug, PartialEq)]struct FLASHR; + struct R; + impl R { } + impl R { } + impl R { } + } + } + mod deviceaddrtype { + #[derive(Debug, PartialEq)]struct DEVICEADDRTYPER; + struct R; + impl R { } + impl R { } + impl R { } + } + } + mod bprot { + mod config0 { + #[derive(Debug, PartialEq)]struct REGION0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION2R; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION3R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION4R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION5R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION6R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION7R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION8R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION9R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION10R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION11R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION12R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION13R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION14R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION15R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION16R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION17R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION18R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION19R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION20R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION21R; + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION22R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION23R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION24R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION25R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION26R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION27R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION28R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION29R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION30R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct REGION31R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod disableindebug { + struct R; + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct DISABLEINDEBUGR; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod twi0 { + mod psel { + mod scl { + struct R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + impl R { } + + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod sda { + struct R; + impl R { } + impl R { } + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod events_stopped { + #[derive(Debug, PartialEq)]struct EVENTS_STOPPEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_txdsent { + #[derive(Debug, PartialEq)]struct EVENTS_TXDSENTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_bb { + #[derive(Debug, PartialEq)]struct EVENTS_BBR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod shorts { + #[derive(Debug, PartialEq)]struct BB_SUSPENDR; + #[derive(Debug, PartialEq)]struct BB_STOPR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenclr { + #[derive(Debug, PartialEq)]struct STOPPEDR; + #[derive(Debug, PartialEq)]struct RXDREADYR; + #[derive(Debug, PartialEq)]struct TXDSENTR; + #[derive(Debug, PartialEq)]struct ERRORR; + #[derive(Debug, PartialEq)]struct BBR; + #[derive(Debug, PartialEq)]struct SUSPENDEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod enable { + #[derive(Debug, PartialEq)]struct ENABLER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod frequency { + #[derive(Debug, PartialEq)]struct FREQUENCYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod twim0 { + mod psel { + mod scl { + #[derive(Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod sda { + #[derive(Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod txd { + mod ptr { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod maxcnt { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod amount { + struct R; + impl R { } + impl R { } + impl R { } + } + mod list { + #[derive(Debug, PartialEq)]struct LISTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod tasks_suspend { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_error { + #[derive(Debug, PartialEq)]struct EVENTS_ERRORR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_rxstarted { + #[derive(Debug, PartialEq)]struct EVENTS_RXSTARTEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_lastrx { + #[derive(Debug, PartialEq)]struct EVENTS_LASTRXR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod shorts { + #[derive(Debug, PartialEq)]struct LASTTX_STARTRXR; + #[derive(Debug, PartialEq)]struct LASTTX_SUSPENDR; + #[derive(Debug, PartialEq)]struct LASTTX_STOPR; + #[derive(Debug, PartialEq)]struct LASTRX_STARTTXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct LASTRX_SUSPENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct LASTRX_STOPR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenset { + #[derive(Clone, Copy, Debug, PartialEq)]struct STOPPEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ERRORR; + #[derive(Clone, Copy, Debug, PartialEq)]struct SUSPENDEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct RXSTARTEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct TXSTARTEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct LASTRXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct LASTTXR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod errorsrc { + #[derive(Clone, Copy, Debug, PartialEq)]struct OVERRUNR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ANACKR; + #[derive(Clone, Copy, Debug, PartialEq)]struct DNACKR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod frequency { + #[derive(Clone, Copy, Debug, PartialEq)]struct FREQUENCYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod twis0 { + mod psel { + mod scl { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod sda { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod txd { + mod ptr{ + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod maxcnt { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod amount { + struct R; + impl R { } + impl R { } + impl R { } + } + mod list { + #[derive(Clone, Copy, Debug, PartialEq)]struct LISTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod events_stopped { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_STOPPEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_rxstarted { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_RXSTARTEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_write { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_WRITER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod shorts { + #[derive(Clone, Copy, Debug, PartialEq)]struct WRITE_SUSPENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct READ_SUSPENDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenset { + #[derive(Clone, Copy, Debug, PartialEq)]struct STOPPEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ERRORR; + #[derive(Clone, Copy, Debug, PartialEq)]struct RXSTARTEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct TXSTARTEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct WRITER; + #[derive(Clone, Copy, Debug, PartialEq)]struct READR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod errorsrc { + #[derive(Debug, PartialEq)]struct OVERFLOWR; + #[derive(Clone, Copy, Debug, PartialEq)]struct DNACKR; + #[derive(Clone, Copy, Debug, PartialEq)]struct OVERREADR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod enable { + #[derive(Clone, Copy, Debug, PartialEq)]struct ENABLER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod config { + #[derive(Clone, Copy, Debug, PartialEq)]struct ADDRESS0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct ADDRESS1R; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod spi0 { + mod psel { + mod sck { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + + } + mod mosi { + #[derive(Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod miso { + #[derive(Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod intenset { + #[derive(Debug, PartialEq)]struct READYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod enable { + #[derive(Debug, PartialEq)]struct ENABLER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod config { + #[derive(Clone, Copy, Debug, PartialEq)]struct ORDERR; + #[derive(Debug, PartialEq)]struct CPHAR; + #[derive(Clone, Copy, Debug, PartialEq)]struct CPOLR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod spim0 { + mod psel { + mod sck { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod mosi { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod miso { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod txd { + mod ptr { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod maxcnt { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod amount { + struct R; + impl R { } + impl R { } + impl R { } + } + mod list { + #[derive(Clone, Copy, Debug, PartialEq)]struct LISTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod tasks_resume { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_stopped { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_STOPPEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_end { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_ENDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_started { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_STARTEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenset { + #[derive(Clone, Copy, Debug, PartialEq)]struct STOPPEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDRXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDTXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct STARTEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenclr { + #[derive(Clone, Copy, Debug, PartialEq)]struct STOPPEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDRXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDTXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct STARTEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod frequency { + #[derive(Clone, Copy, Debug, PartialEq)]struct FREQUENCYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod spis0 { + mod psel { + mod sck { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod miso { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod mosi { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod csn { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod txd { + mod ptr { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod maxcnt { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod amount { + struct R; + impl R { } + impl R { } + impl R { } + } + mod list { + #[derive(Clone, Copy, Debug, PartialEq)]struct LISTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod events_endrx { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_ENDRXR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod shorts { + #[derive(Clone, Copy, Debug, PartialEq)]struct END_ACQUIRER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenclr { + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ENDRXR; + #[derive(Clone, Copy, Debug, PartialEq)]struct ACQUIREDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod status { + #[derive(Clone, Copy, Debug, PartialEq)]struct OVERREADR; + #[derive(Clone, Copy, Debug, PartialEq)]struct OVERFLOWR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod config { + #[derive(Clone, Copy, Debug, PartialEq)]struct ORDERR; + #[derive(Clone, Copy, Debug, PartialEq)]struct CPHAR; + #[derive(Clone, Copy, Debug, PartialEq)]struct CPOLR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + pub struct TIMER0; + mod timer0 { + mod shorts { + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE0_CLEARR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE1_CLEARR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE2_CLEARR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE3_CLEARR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE4_CLEARR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE5_CLEARR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE0_STOPR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE1_STOPR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE2_STOPR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE3_STOPR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE4_STOPR; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE5_STOPR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenclr { + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE2R; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE3R; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE4R; + #[derive(Clone, Copy, Debug, PartialEq)]struct COMPARE5R; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod bitmode { + #[derive(Clone, Copy, Debug, PartialEq)]struct BITMODER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod temp { + mod events_datardy { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_DATARDYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenset { + #[derive(Clone, Copy, Debug, PartialEq)]struct DATARDYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod rng { + mod events_valrdy { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_VALRDYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenset { + #[derive(Clone, Copy, Debug, PartialEq)]struct VALRDYR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod config { + #[derive(Clone, Copy, Debug, PartialEq)]struct DERCENR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod egu0 { + mod inten { + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED2R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED3R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED4R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED5R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED6R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED7R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED8R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED9R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED10R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED11R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED12R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED13R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED14R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED15R; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenclr { + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED2R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED3R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED4R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED5R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED6R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED7R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED8R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED9R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED10R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED11R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED12R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED13R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED14R; + #[derive(Clone, Copy, Debug, PartialEq)]struct TRIGGERED15R; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod pwm0 { + mod psel { + mod out { + #[derive(Clone, Copy, Debug, PartialEq)]struct CONNECTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod tasks_seqstart { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + } + mod tasks_nextstep { + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_stopped { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_STOPPEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_seqstarted { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_SEQSTARTEDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_seqend { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_SEQENDR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod events_loopsdone { + #[derive(Clone, Copy, Debug, PartialEq)]struct EVENTS_LOOPSDONER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod inten { + #[derive(Clone, Copy, Debug, PartialEq)]struct STOPPEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQSTARTED0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQSTARTED1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQEND0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQEND1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct PWMPERIODENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct LOOPSDONER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod intenclr { + #[derive(Clone, Copy, Debug, PartialEq)]struct STOPPEDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQSTARTED0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQSTARTED1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQEND0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct SEQEND1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct PWMPERIODENDR; + #[derive(Clone, Copy, Debug, PartialEq)]struct LOOPSDONER; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod mode { + #[derive(Clone, Copy, Debug, PartialEq)]struct UPDOWNR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod prescaler { + #[derive(Clone, Copy, Debug, PartialEq)]struct PRESCALERR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + mod loop_ { + #[derive(Clone, Copy, Debug, PartialEq)]struct CNTR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod nvmc { + mod ready { + #[derive(Clone, Copy, Debug, PartialEq)]struct READYR; + struct R; + impl R { } + impl R { } + impl R { } + } + mod eraseuicr { + #[derive(Clone, Copy, Debug, PartialEq)]struct ERASEUICRR; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } + mod ppi { + mod chg { + #[derive(Clone, Copy, Debug, PartialEq)]struct CH0R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH1R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH2R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH3R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH4R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH5R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH6R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH7R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH8R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH9R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH10R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH11R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH12R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH13R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH14R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH15R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH16R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH17R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH18R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH19R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH20R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH21R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH22R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH23R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH24R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH25R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH26R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH27R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH28R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH29R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH30R; + #[derive(Clone, Copy, Debug, PartialEq)]struct CH31R; + struct R; + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + impl R { } + } + } +} diff --git a/src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v1 b/src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v1 new file mode 100644 index 0000000000000..5a85088411bca --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v1 @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +mod ble { + pub mod link { + #[cfg(not_now)] + pub fn process_data_packet() {} + + pub fn process_data_packet() { + use core::fmt::Write; + fn get() -> crate::ble::Duration { + unimplemented!() + } + let _ = write!(crate::Logger, "{}", get()); + } + } + + use core::fmt::{self, Debug, Display}; + + pub struct Duration(pub u32); + + impl Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.0 >= 1000 { + let (millis, submilli_micros) = (self.0 / 1000, self.0 % 1000); + write!(f, "{}", millis) + } else { + write!(f, "0") + } + } + } + + impl Debug for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(self, f) + } + } +} + +use core::fmt::{self, Write}; +use core::panic::PanicInfo; + +struct Logger; + +impl Write for Logger { + fn write_str(&mut self, _s: &str) -> fmt::Result { + loop { } + } +} + +#[export_name = "main"] +fn main() { + let _: nrf52810_hal::nrf52810_pac::TIMER0; + let _ = write!(Logger, "{:?}", None::); + crate::ble::link::process_data_packet(); +} + +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + loop {} +} diff --git a/src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v2 b/src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v2 new file mode 100644 index 0000000000000..df174f214dfc2 --- /dev/null +++ b/src/test/run-make/removing-code-and-incremental-lto/rubble.rs.v2 @@ -0,0 +1,65 @@ +#![no_std] +#![no_main] + +mod ble { + pub mod link { + pub fn process_data_packet() {} + + #[cfg(not_now)] + pub fn process_data_packet() { + use core::fmt::Write; + fn get() -> crate::ble::Duration { + unimplemented!() + } + let _ = write!(crate::Logger, "{}", get()); + } + } + + use core::fmt::{self, Debug, Display}; + + pub struct Duration(pub u32); + + impl Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.0 >= 1000 { + let (millis, submilli_micros) = (self.0 / 1000, self.0 % 1000); + if submilli_micros == 0 { + write!(f, "{}", millis) + } else { + write!(f, "{}{}", 0, 0) + } + } else { + write!(f, "0") + } + } + } + + impl Debug for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(self, f) + } + } +} + +use core::fmt::{self, Write}; +use core::panic::PanicInfo; + +struct Logger; + +impl Write for Logger { + fn write_str(&mut self, _s: &str) -> fmt::Result { + loop { } + } +} + +#[export_name = "main"] +fn main() { + let _: nrf52810_hal::nrf52810_pac::TIMER0; + let _ = write!(Logger, "{:?}", None::); + crate::ble::link::process_data_packet(); +} + +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + loop {} +} From 1d594c4348b862b8d29d37bd1f57aa830ca7e68e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 4 Dec 2019 14:25:05 +0100 Subject: [PATCH 03/13] do not run the test for targets that it was not checked against. however, make sure run-make test actually runs, too. (The `# only-target` thing does not work, at least not for cross-compilation. See issue 67018.)) --- .../run-make/removing-code-and-incremental-lto/Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/run-make/removing-code-and-incremental-lto/Makefile b/src/test/run-make/removing-code-and-incremental-lto/Makefile index 5b9387e6e8a05..ba9a2a40cff36 100644 --- a/src/test/run-make/removing-code-and-incremental-lto/Makefile +++ b/src/test/run-make/removing-code-and-incremental-lto/Makefile @@ -5,6 +5,15 @@ # $ ./x.py test --target thumbv7m-none-eabi src/test/run-make # The original target of interest was thumbv7em-none-eabi +# +# It is possible that other targets could be tested, but as long as we are +# testing *something*, that is enough for me. (Note that you should not just add +# `# only-target` lines; see rust-lang/rust#67018.) +ifeq (,$(filter $(TARGET),thumbv6m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf thumbv7m-none-eabi)) + +all: + +else # This is a test of a scenario that arose in issue rust-lang/rust#59535. # From f29e4bde8623c99f5a94ae46fa5063a2cc30302b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 5 Dec 2019 13:42:14 +0100 Subject: [PATCH 04/13] Remove over-zealous assertion in my PR: just do replacement unconditionally. --- src/librustc_codegen_llvm/back/lto.rs | 24 ++++--------------- .../Makefile | 9 +++++++ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 4d2973b7f753e..65c743aabfa1f 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -872,28 +872,14 @@ pub struct ThinLTOImports { impl ThinLTOImports { /// Records `llvm_module_name` as importing `prev_imports` rather than what - /// we have currently computed. Ensures that the previous imports are a - /// superset of what imports LLVM currently computed. + /// we have currently computed. fn overwrite_with_past_import_state(&mut self, llvm_module_name: &str, prev_imports: &[String]) { - // There were imports used to previous LTO optimize the module; make - // sure they are a superset of whatever we have in our current state, - // and then store them as our new current state. - let curr_imports_for_mod = self.modules_imported_by(llvm_module_name); - for imported in curr_imports_for_mod { - assert!(prev_imports.contains(imported)); - } - // We can avoid doing the insertion entirely if the sets are equivalent, - // and we can determine equivalence cheaply via a length comparison, - // since we have already asserted the past state to be a superset of the - // current state. - if prev_imports.len() != curr_imports_for_mod.len() { - debug!("ThinLTOImports::restore_past_import_state \ - mod: {:?} replacing curr state: {:?} with prev state: {:?}", - llvm_module_name, curr_imports_for_mod, prev_imports); - self.imports.insert(llvm_module_name.to_owned(), prev_imports.to_vec()); - } + debug!("ThinLTOImports::overwrite_with_past_import_state \ + mod: {:?} replacing curr state: {:?} with prev state: {:?}", + llvm_module_name, self.modules_imported_by(llvm_module_name), prev_imports); + self.imports.insert(llvm_module_name.to_owned(), prev_imports.to_vec()); } fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] { diff --git a/src/test/run-make/removing-code-and-incremental-lto/Makefile b/src/test/run-make/removing-code-and-incremental-lto/Makefile index ba9a2a40cff36..4d87781c4cb4e 100644 --- a/src/test/run-make/removing-code-and-incremental-lto/Makefile +++ b/src/test/run-make/removing-code-and-incremental-lto/Makefile @@ -76,3 +76,12 @@ all: $(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE1_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2 cp rubble.rs.v2 $(TMPDIR)/rubble.rs $(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE2_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2 + echo Now testing the reverse direction + rm -rf $(INCREMENTAL_DIR) + mkdir -p $(INCREMENTAL_DIR) + cp rubble.rs.v2 $(TMPDIR)/rubble.rs + $(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE2_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2 + cp rubble.rs.v1 $(TMPDIR)/rubble.rs + $(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE1_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2 + +endif From 2a83379af9a5caf14378a331cbd5f83ffc7a5f07 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 5 Dec 2019 22:33:09 +0100 Subject: [PATCH 05/13] Switch to simpler strategy devised after discussing with mw. Instead of accumulating (and acting upon) LTO import information over an unbounded number of prior compilations, just see if the curent import set matches the previous import set. if they don't match, then you cannot reuse the PostLTO build product for that module. --- src/librustc_codegen_llvm/back/lto.rs | 57 ++++++++++++++++----------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 65c743aabfa1f..d93fd3b08edea 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -479,7 +479,7 @@ fn thin_lto(cgcx: &CodegenContext, info!("thin LTO data created"); - let (import_map_path, prev_import_map, mut curr_import_map) = + let (import_map_path, prev_import_map, curr_import_map) = if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir { let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME); @@ -522,35 +522,39 @@ fn thin_lto(cgcx: &CodegenContext, for (module_index, module_name) in shared.module_names.iter().enumerate() { let module_name = module_name_to_str(module_name); - // If the module hasn't changed, and none of the modules it imported - // from (*) has changed, then we can (1.) re-use the post-ThinLTO - // version of the module, and thus (2.) save those previous ThinLTO - // imports as its current set of imports. + // If (1.) the module hasn't changed, and (2.) none of the modules + // it imports from has changed, *and* (3.) the import-set itself has + // not changed from the previous compile when it was last + // ThinLTO'ed, then we can re-use the post-ThinLTO version of the + // module. Otherwise, freshly perform LTO optimization. // - // (*): that is, "imported from" at the time it was previously ThinLTO'ed; - // see rust-lang/rust#59535. + // This strategy means we can always save the computed imports as + // canon: when we reuse the post-ThinLTO version, condition (3.) + // ensures that the curent import set is the same as the previous + // one. (And of course, when we don't reuse the post-ThinLTO + // version, the current import set *is* the correct one, since we + // are doing the ThinLTO in this current compilation cycle.) // - // If we do *not* re-use the post-ThinLTO version of the module, - // then we should save the computed imports that LLVM reported to us - // during this cycle. + // See rust-lang/rust#59535. if green_modules.contains_key(module_name) { assert!(cgcx.incr_comp_session_dir.is_some()); assert!(prev_import_map.is_some()); let prev_import_map = prev_import_map.as_ref().unwrap(); + let prev_imports = prev_import_map.modules_imported_by(module_name); - let imports_all_green = prev_imports + let curr_imports = curr_import_map.modules_imported_by(module_name); + let imports_all_green = curr_imports .iter() .all(|imported_module| green_modules.contains_key(imported_module)); - if imports_all_green { + if imports_all_green && equivalent_as_sets(prev_imports, curr_imports) { let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); assert!(cgcx.incr_comp_session_dir.is_some()); cgcx.cgu_reuse_tracker.set_actual_reuse(module_name, CguReuse::PostLto); - curr_import_map.overwrite_with_past_import_state(module_name, prev_imports); continue } } @@ -574,6 +578,22 @@ fn thin_lto(cgcx: &CodegenContext, } } +/// Given two slices, each with no repeat elements. returns true if and only if +/// the two slices have the same contents when considered as sets (i.e. when +/// element order is disregarded). +fn equivalent_as_sets(a: &[String], b: &[String]) -> bool { + // cheap path: unequal lengths means cannot possibly be set equivalent. + if a.len() != b.len() { return false; } + // fast path: before taking time to build up sorted clones, just see if the given inputs are equivant as is. + if a == b { return true; } + // slow path: compare sorted contents + let mut a: Vec<&str> = a.iter().map(|s| s.as_str()).collect(); + let mut b: Vec<&str> = b.iter().map(|s| s.as_str()).collect(); + a.sort(); + b.sort(); + a == b +} + pub(crate) fn run_pass_manager(cgcx: &CodegenContext, module: &ModuleCodegen, config: &ModuleConfig, @@ -871,17 +891,6 @@ pub struct ThinLTOImports { } impl ThinLTOImports { - /// Records `llvm_module_name` as importing `prev_imports` rather than what - /// we have currently computed. - fn overwrite_with_past_import_state(&mut self, - llvm_module_name: &str, - prev_imports: &[String]) { - debug!("ThinLTOImports::overwrite_with_past_import_state \ - mod: {:?} replacing curr state: {:?} with prev state: {:?}", - llvm_module_name, self.modules_imported_by(llvm_module_name), prev_imports); - self.imports.insert(llvm_module_name.to_owned(), prev_imports.to_vec()); - } - fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] { self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) } From 7f485fb7c113e641d786d862eb8febd1fe149d8a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 5 Dec 2019 22:42:25 +0100 Subject: [PATCH 06/13] placate tidy. --- src/librustc_codegen_llvm/back/lto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index d93fd3b08edea..7b72ba2f3dcf6 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -584,7 +584,7 @@ fn thin_lto(cgcx: &CodegenContext, fn equivalent_as_sets(a: &[String], b: &[String]) -> bool { // cheap path: unequal lengths means cannot possibly be set equivalent. if a.len() != b.len() { return false; } - // fast path: before taking time to build up sorted clones, just see if the given inputs are equivant as is. + // fast path: before sorting, check if inputs are equivalent as is. if a == b { return true; } // slow path: compare sorted contents let mut a: Vec<&str> = a.iter().map(|s| s.as_str()).collect(); From f7ccfc642774a0c1f6d738c122cd719aa685d5e8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 6 Dec 2019 15:56:26 +0100 Subject: [PATCH 07/13] Review feedbacK: conservatively respond to file missing and IO errors. --- src/librustc_codegen_llvm/back/lto.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 7b72ba2f3dcf6..b33418640b149 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -483,11 +483,11 @@ fn thin_lto(cgcx: &CodegenContext, if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir { let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME); + // If previous imports have been deleted, or we get an IO error + // reading the file storing them, then we'll just use `None` as the + // prev_import_map, which will force the code to be recompiled. let prev = if path.exists() { - // FIXME: can/should we recover from IO error occuring here - // (e.g. by clearing green_modules), rather than panicking from - // unwrap call? - Some(ThinLTOImports::load_from_file(&path).unwrap()) + ThinLTOImports::load_from_file(&path).ok() } else { None }; @@ -536,11 +536,10 @@ fn thin_lto(cgcx: &CodegenContext, // are doing the ThinLTO in this current compilation cycle.) // // See rust-lang/rust#59535. - if green_modules.contains_key(module_name) { + if let (Some(prev_import_map), true) = + (prev_import_map.as_ref(), green_modules.contains_key(module_name)) + { assert!(cgcx.incr_comp_session_dir.is_some()); - assert!(prev_import_map.is_some()); - - let prev_import_map = prev_import_map.as_ref().unwrap(); let prev_imports = prev_import_map.modules_imported_by(module_name); let curr_imports = curr_import_map.modules_imported_by(module_name); From 60c07c84cfdaa0a203dffbc2c649dbab79924aa2 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 6 Dec 2019 15:56:48 +0100 Subject: [PATCH 08/13] review feedback: fix comment. --- src/librustc_codegen_llvm/back/lto.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index b33418640b149..19ddbcba0bfd7 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -565,7 +565,8 @@ fn thin_lto(cgcx: &CodegenContext, })); } - // Save the accumulated ThinLTO import information + // Save the curent ThinLTO import information for the next compilation + // session, overwriting the previous serialized imports (if any). if let Some(path) = import_map_path { if let Err(err) = curr_import_map.save_to_file(&path) { let msg = format!("Error while writing ThinLTO import data: {}", err); From 405720a2d5ef421453becf82020c2b557aa9fcc6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 6 Dec 2019 15:57:27 +0100 Subject: [PATCH 09/13] review feedback: use `FxHashSet` rather than sorted `Vec` for set-comparison slow-path. --- src/librustc_codegen_llvm/back/lto.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 19ddbcba0bfd7..4e65e435d96a2 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -15,7 +15,7 @@ use rustc::hir::def_id::LOCAL_CRATE; use rustc::middle::exported_symbols::SymbolExportLevel; use rustc::session::config::{self, Lto}; use rustc::util::common::time_ext; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use rustc_codegen_ssa::{RLIB_BYTECODE_EXTENSION, ModuleCodegen, ModuleKind}; use std::ffi::{CStr, CString}; @@ -584,13 +584,11 @@ fn thin_lto(cgcx: &CodegenContext, fn equivalent_as_sets(a: &[String], b: &[String]) -> bool { // cheap path: unequal lengths means cannot possibly be set equivalent. if a.len() != b.len() { return false; } - // fast path: before sorting, check if inputs are equivalent as is. + // fast path: before building new things, check if inputs are equivalent as is. if a == b { return true; } - // slow path: compare sorted contents - let mut a: Vec<&str> = a.iter().map(|s| s.as_str()).collect(); - let mut b: Vec<&str> = b.iter().map(|s| s.as_str()).collect(); - a.sort(); - b.sort(); + // slow path: general set comparison. + let a: FxHashSet<&str> = a.iter().map(|s| s.as_str()).collect(); + let b: FxHashSet<&str> = b.iter().map(|s| s.as_str()).collect(); a == b } From aecb51149dbe2f3e8745c646ebfdc992624188cf Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 6 Dec 2019 15:57:52 +0100 Subject: [PATCH 10/13] General purpose teest cases contributed by mw. --- .../cgu_invalidated_when_import_added.rs | 62 ++++++++++++++++ .../cgu_invalidated_when_import_removed.rs | 74 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs create mode 100644 src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs new file mode 100644 index 0000000000000..42168dd273eff --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs @@ -0,0 +1,62 @@ +// revisions: cfail1 cfail2 +// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10 +// build-pass + +// rust-lang/rust#59535: +// +// This is analgous to cgu_invalidated_when_import_removed.rs, but it covers +// the other direction: +// +// We start with a call-graph like `[A] -> [B -> D] [C]` (where the letters are +// functions and the modules are enclosed in `[]`), and add a new call `D <- C`, +// yielding the new call-graph: `[A] -> [B -> D] <- [C]` +// +// The effect of this is that the compiler previously classfied `D` as internal +// and the import-set of `[A]` to be just `B`. But after adding the `D <- C` call, +// `D` is no longer classified as internal, and the import-set of `[A]` becomes +// both `B` and `D`. +// +// We check this case because an early proposed pull request included an +// assertion that the import-sets monotonically decreased over time, a claim +// which this test case proves to be false. + +fn main() { + foo::foo(); + bar::baz(); +} + +mod foo { + + // In cfail1, ThinLTO decides that foo() does not get inlined into main, and + // instead bar() gets inlined into foo(). + // In cfail2, foo() gets inlined into main. + pub fn foo(){ + bar() + } + + // This function needs to be big so that it does not get inlined by ThinLTO + // but *does* get inlined into foo() when it is declared `internal` in + // cfail1 (alone). + pub fn bar(){ + println!("quux1"); + println!("quux2"); + println!("quux3"); + println!("quux4"); + println!("quux5"); + println!("quux6"); + println!("quux7"); + println!("quux8"); + println!("quux9"); + } +} + +mod bar { + + #[inline(never)] + pub fn baz() { + #[cfg(cfail2)] + { + crate::foo::bar(); + } + } +} diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs new file mode 100644 index 0000000000000..19ce7b3e148f7 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs @@ -0,0 +1,74 @@ +// revisions: cfail1 cfail2 +// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10 +// build-pass + +// rust-lang/rust#59535: +// +// Consider a call-graph like `[A] -> [B -> D] <- [C]` (where the letters are +// functions and the modules are enclosed in `[]`) +// +// In our specific instance, the earlier compilations were inlining the call +// to`B` into `A`; thus `A` ended up with a external reference to the symbol `D` +// in its object code, to be resolved at subsequent link time. The LTO import +// information provided by LLVM for those runs reflected that information: it +// explicitly says during those runs, `B` definition and `D` declaration were +// imported into `[A]`. +// +// The change between incremental builds was that the call `D <- C` was removed. +// +// That change, coupled with other decisions within `rustc`, made the compiler +// decide to make `D` an internal symbol (since it was no longer accessed from +// other codegen units, this makes sense locally). And then the definition of +// `D` was inlined into `B` and `D` itself was eliminated entirely. +// +// The current LTO import information reported that `B` alone is imported into +// `[A]` for the *current compilation*. So when the Rust compiler surveyed the +// dependence graph, it determined that nothing `[A]` imports changed since the +// last build (and `[A]` itself has not changed either), so it chooses to reuse +// the object code generated during the previous compilation. +// +// But that previous object code has an unresolved reference to `D`, and that +// causes a link time failure! + +fn main() { + foo::foo(); + bar::baz(); +} + +mod foo { + + // In cfail1, foo() gets inlined into main. + // In cfail2, ThinLTO decides that foo() does not get inlined into main, and + // instead bar() gets inlined into foo(). But faulty logic in our incr. + // ThinLTO implementation thought that `main()` is unchanged and thus reused + // the object file still containing a call to the now non-existant bar(). + pub fn foo(){ + bar() + } + + // This function needs to be big so that it does not get inlined by ThinLTO + // but *does* get inlined into foo() once it is declared `internal` in + // cfail2. + pub fn bar(){ + println!("quux1"); + println!("quux2"); + println!("quux3"); + println!("quux4"); + println!("quux5"); + println!("quux6"); + println!("quux7"); + println!("quux8"); + println!("quux9"); + } +} + +mod bar { + + #[inline(never)] + pub fn baz() { + #[cfg(cfail1)] + { + crate::foo::bar(); + } + } +} From 3980342f3164a62ba7036711c16cc8af20d06418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 3 Dec 2019 22:19:18 -0800 Subject: [PATCH 11/13] Use structured suggestion for disambiguating method calls Fix #65635. --- src/librustc/ty/mod.rs | 11 ++ src/librustc_typeck/check/expr.rs | 3 +- src/librustc_typeck/check/method/suggest.rs | 127 +++++++++++++----- .../associated-const-ambiguity-report.stderr | 10 +- src/test/ui/error-codes/E0034.stderr | 10 +- .../inference_unstable_featured.stderr | 10 +- src/test/ui/issues/issue-18446.stderr | 6 +- src/test/ui/issues/issue-3702-2.stderr | 10 +- .../issue-65634-raw-ident-suggestion.stderr | 10 +- ...method-ambig-two-traits-cross-crate.stderr | 10 +- ...method-ambig-two-traits-from-bounds.stderr | 10 +- .../method-ambig-two-traits-from-impls.stderr | 10 +- ...method-ambig-two-traits-from-impls2.stderr | 10 +- ...mbig-two-traits-with-default-method.stderr | 10 +- ...e-trait-object-with-separate-params.stderr | 15 ++- src/test/ui/span/issue-37767.stderr | 30 ++++- src/test/ui/span/issue-7575.stderr | 20 ++- .../ui/traits/trait-alias-ambiguous.stderr | 10 +- 18 files changed, 248 insertions(+), 74 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 78a31f4e54466..15bbfa7860fa7 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -212,6 +212,17 @@ pub enum AssocKind { Type } +impl AssocKind { + pub fn suggestion_descr(&self) -> &'static str { + match self { + ty::AssocKind::Method => "method call", + ty::AssocKind::Type | + ty::AssocKind::OpaqueTy => "associated type", + ty::AssocKind::Const => "associated constant", + } + } +} + impl AssocItem { pub fn def_kind(&self) -> DefKind { match self.kind { diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 5bfc60c754067..b11a8a7ab5336 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1422,8 +1422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: ast::Ident, ) -> Ty<'tcx> { let expr_t = self.check_expr_with_needs(base, needs); - let expr_t = self.structurally_resolved_type(base.span, - expr_t); + let expr_t = self.structurally_resolved_type(base.span, expr_t); let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); while let Some((base_t, _)) = autoderef.next() { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index f4b53b4d10604..cd26e6f237c7d 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -82,34 +82,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let print_disambiguation_help = | err: &mut DiagnosticBuilder<'_>, trait_name: String, + rcvr_ty: Ty<'_>, + kind: ty::AssocKind, + span: Span, + candidate: Option, | { - err.help(&format!( - "to disambiguate the method call, write `{}::{}({}{})` instead", - trait_name, - item_name, - if rcvr_ty.is_region_ptr() && args.is_some() { - if rcvr_ty.is_mutable_ptr() { - "&mut " + let mut applicability = Applicability::MachineApplicable; + let sugg_args = if let ty::AssocKind::Method = kind { + format!( + "({}{})", + if rcvr_ty.is_region_ptr() && args.is_some() { + if rcvr_ty.is_mutable_ptr() { + "&mut " + } else { + "&" + } } else { - "&" - } - } else { - "" - }, - args.map(|arg| arg - .iter() - .map(|arg| self.tcx.sess.source_map().span_to_snippet(arg.span) - .unwrap_or_else(|_| "...".to_owned())) - .collect::>() - .join(", ") - ).unwrap_or_else(|| "...".to_owned()) - )); + "" + }, + args.map(|arg| arg + .iter() + .map(|arg| self.tcx.sess.source_map().span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "...".to_owned() + })) + .collect::>() + .join(", ") + ).unwrap_or_else(|| { + applicability = Applicability::HasPlaceholders; + "...".to_owned() + }), + ) + } else { + String::new() + }; + let sugg = format!("{}::{}{}", trait_name, item_name, sugg_args); + err.span_suggestion( + span, + &format!( + "disambiguate the {} for {}", + kind.suggestion_descr(), + if let Some(candidate) = candidate { + format!("candidate #{}", candidate) + } else { + "the candidate".to_string() + }, + ), + sugg, + applicability, + ); }; let report_candidates = | span: Span, err: &mut DiagnosticBuilder<'_>, mut sources: Vec, + sugg_span: Span, | { sources.sort(); sources.dedup(); @@ -150,15 +179,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let note_str = if sources.len() > 1 { - format!("candidate #{} is defined in an impl{} for the type `{}`", - idx + 1, - insertion, - impl_ty) + let (note_str, idx) = if sources.len() > 1 { + (format!( + "candidate #{} is defined in an impl{} for the type `{}`", + idx + 1, + insertion, + impl_ty, + ), Some(idx + 1)) } else { - format!("the candidate is defined in an impl{} for the type `{}`", - insertion, - impl_ty) + (format!( + "the candidate is defined in an impl{} for the type `{}`", + insertion, + impl_ty, + ), None) }; if let Some(note_span) = note_span { // We have a span pointing to the method. Show note with snippet. @@ -168,7 +201,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.note(¬e_str); } if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { - print_disambiguation_help(err, self.tcx.def_path_str(trait_ref.def_id)); + let path = self.tcx.def_path_str(trait_ref.def_id); + + let ty = match item.kind { + ty::AssocKind::Const | + ty::AssocKind::Type | + ty::AssocKind::OpaqueTy => rcvr_ty, + ty::AssocKind::Method => self.tcx.fn_sig(item.def_id) + .inputs() + .skip_binder() + .get(0) + .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) + .map(|ty| *ty) + .unwrap_or(rcvr_ty), + }; + print_disambiguation_help(err, path, ty, item.kind, sugg_span, idx); } } CandidateSource::TraitSource(trait_did) => { @@ -182,19 +229,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let item_span = self.tcx.sess.source_map() .def_span(self.tcx.def_span(item.def_id)); - if sources.len() > 1 { + let idx = if sources.len() > 1 { span_note!(err, item_span, "candidate #{} is defined in the trait `{}`", idx + 1, self.tcx.def_path_str(trait_did)); + Some(idx + 1) } else { span_note!(err, item_span, "the candidate is defined in the trait `{}`", self.tcx.def_path_str(trait_did)); - } - print_disambiguation_help(err, self.tcx.def_path_str(trait_did)); + None + }; + let path = self.tcx.def_path_str(trait_did); + print_disambiguation_help(err, path, rcvr_ty, item.kind, sugg_span, idx); } } } @@ -203,6 +253,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let sugg_span = if let SelfSource::MethodCall(expr) = source { + // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. + self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span + } else { + span + }; + match error { MethodError::NoMatch(NoMatchData { static_candidates: static_sources, @@ -495,9 +552,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - report_candidates(span, &mut err, static_sources); + report_candidates(span, &mut err, static_sources, sugg_span); } else if static_sources.len() > 1 { - report_candidates(span, &mut err, static_sources); + report_candidates(span, &mut err, static_sources, sugg_span); } if !unsatisfied_predicates.is_empty() { @@ -584,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "multiple applicable items in scope"); err.span_label(span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, sources); + report_candidates(span, &mut err, sources, sugg_span); err.emit(); } diff --git a/src/test/ui/associated-const/associated-const-ambiguity-report.stderr b/src/test/ui/associated-const/associated-const-ambiguity-report.stderr index bb217bd182db6..92a8d19021a2c 100644 --- a/src/test/ui/associated-const/associated-const-ambiguity-report.stderr +++ b/src/test/ui/associated-const/associated-const-ambiguity-report.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `Foo` for the type `i32` | LL | const ID: i32 = 1; | ^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `Foo::ID(...)` instead note: candidate #2 is defined in an impl of the trait `Bar` for the type `i32` --> $DIR/associated-const-ambiguity-report.rs:14:5 | LL | const ID: i32 = 3; | ^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `Bar::ID(...)` instead +help: disambiguate the associated constant for candidate #1 + | +LL | const X: i32 = Foo::ID; + | ^^^^^^^ +help: disambiguate the associated constant for candidate #2 + | +LL | const X: i32 = Bar::ID; + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0034.stderr b/src/test/ui/error-codes/E0034.stderr index a58d16bfafb59..30b44fd402bb8 100644 --- a/src/test/ui/error-codes/E0034.stderr +++ b/src/test/ui/error-codes/E0034.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `Trait1` for the type `Tes | LL | fn foo() {} | ^^^^^^^^ - = help: to disambiguate the method call, write `Trait1::foo(...)` instead note: candidate #2 is defined in an impl of the trait `Trait2` for the type `Test` --> $DIR/E0034.rs:16:5 | LL | fn foo() {} | ^^^^^^^^ - = help: to disambiguate the method call, write `Trait2::foo(...)` instead +help: disambiguate the method call for candidate #1 + | +LL | Trait1::foo(...)() + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | Trait2::foo(...)() + | ^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/inference/inference_unstable_featured.stderr b/src/test/ui/inference/inference_unstable_featured.stderr index b06a6298a571c..fa908440e41ea 100644 --- a/src/test/ui/inference/inference_unstable_featured.stderr +++ b/src/test/ui/inference/inference_unstable_featured.stderr @@ -5,9 +5,15 @@ LL | assert_eq!('x'.ipu_flatten(), 0); | ^^^^^^^^^^^ multiple `ipu_flatten` found | = note: candidate #1 is defined in an impl of the trait `inference_unstable_iterator::IpuIterator` for the type `char` - = help: to disambiguate the method call, write `inference_unstable_iterator::IpuIterator::ipu_flatten('x')` instead = note: candidate #2 is defined in an impl of the trait `inference_unstable_itertools::IpuItertools` for the type `char` - = help: to disambiguate the method call, write `inference_unstable_itertools::IpuItertools::ipu_flatten('x')` instead +help: disambiguate the method call for candidate #1 + | +LL | assert_eq!(inference_unstable_iterator::IpuIterator::ipu_flatten(&'x'), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | assert_eq!(inference_unstable_itertools::IpuItertools::ipu_flatten(&'x'), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-18446.stderr b/src/test/ui/issues/issue-18446.stderr index e6afc4c13a912..3422add9dd96b 100644 --- a/src/test/ui/issues/issue-18446.stderr +++ b/src/test/ui/issues/issue-18446.stderr @@ -2,7 +2,10 @@ error[E0034]: multiple applicable items in scope --> $DIR/issue-18446.rs:18:7 | LL | x.foo(); - | ^^^ multiple `foo` found + | --^^^-- + | | | + | | multiple `foo` found + | help: disambiguate the method call for candidate #2: `T::foo(&x)` | note: candidate #1 is defined in an impl for the type `dyn T` --> $DIR/issue-18446.rs:9:5 @@ -14,7 +17,6 @@ note: candidate #2 is defined in the trait `T` | LL | fn foo(&self); | ^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `T::foo(&x)` instead error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3702-2.stderr b/src/test/ui/issues/issue-3702-2.stderr index 4d0ff750c254c..b18e407c3d464 100644 --- a/src/test/ui/issues/issue-3702-2.stderr +++ b/src/test/ui/issues/issue-3702-2.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `ToPrimitive` for the type | LL | fn to_int(&self) -> isize { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `ToPrimitive::to_int(&self)` instead note: candidate #2 is defined in an impl of the trait `Add` for the type `isize` --> $DIR/issue-3702-2.rs:14:5 | LL | fn to_int(&self) -> isize { *self } | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `Add::to_int(&self)` instead +help: disambiguate the method call for candidate #1 + | +LL | ToPrimitive::to_int(&self) + other.to_int() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | Add::to_int(&self) + other.to_int() + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr b/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr index c7bb653dc1f14..feaf3dc753ffb 100644 --- a/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr +++ b/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `async::r#struct(r#fn {})` instead note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn` --> $DIR/issue-65634-raw-ident-suggestion.rs:10:5 | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `await::r#struct(r#fn {})` instead +help: disambiguate the method call for candidate #1 + | +LL | async::r#struct(&r#fn {}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | await::r#struct(&r#fn {}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr b/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr index 9f46a722a508e..fa3add81a28f5 100644 --- a/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr @@ -9,9 +9,15 @@ note: candidate #1 is defined in an impl of the trait `Me2` for the type `usize` | LL | impl Me2 for usize { fn me(&self) -> usize { *self } } | ^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `Me2::me(1_usize)` instead = note: candidate #2 is defined in an impl of the trait `ambig_impl_2_lib::Me` for the type `usize` - = help: to disambiguate the method call, write `ambig_impl_2_lib::Me::me(1_usize)` instead +help: disambiguate the method call for candidate #1 + | +LL | fn main() { Me2::me(&1_usize); } + | ^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | fn main() { ambig_impl_2_lib::Me::me(&1_usize); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr b/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr index 6c493c67e29d9..b6c81c2377ee4 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in the trait `A` | LL | trait A { fn foo(&self); } | ^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `A::foo(t)` instead note: candidate #2 is defined in the trait `B` --> $DIR/method-ambig-two-traits-from-bounds.rs:2:11 | LL | trait B { fn foo(&self); } | ^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `B::foo(t)` instead +help: disambiguate the method call for candidate #1 + | +LL | A::foo(t); + | ^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | B::foo(t); + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr index 0b3724e030fa4..71c65f7ccc68d 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `A` for the type `AB` | LL | fn foo(self) {} | ^^^^^^^^^^^^ - = help: to disambiguate the method call, write `A::foo(AB {})` instead note: candidate #2 is defined in an impl of the trait `B` for the type `AB` --> $DIR/method-ambig-two-traits-from-impls.rs:11:5 | LL | fn foo(self) {} | ^^^^^^^^^^^^ - = help: to disambiguate the method call, write `B::foo(AB {})` instead +help: disambiguate the method call for candidate #1 + | +LL | A::foo(AB {}); + | ^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | B::foo(AB {}); + | ^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr index 81c99b33c813e..55499215799d7 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `A` for the type `AB` | LL | fn foo() {} | ^^^^^^^^ - = help: to disambiguate the method call, write `A::foo(...)` instead note: candidate #2 is defined in an impl of the trait `B` for the type `AB` --> $DIR/method-ambig-two-traits-from-impls2.rs:11:5 | LL | fn foo() {} | ^^^^^^^^ - = help: to disambiguate the method call, write `B::foo(...)` instead +help: disambiguate the method call for candidate #1 + | +LL | A::foo(...)(); + | ^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | B::foo(...)(); + | ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr b/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr index dc8aef2503739..3dbb17371004a 100644 --- a/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `Foo` for the type `usize` | LL | trait Foo { fn method(&self) {} } | ^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `Foo::method(1_usize)` instead note: candidate #2 is defined in an impl of the trait `Bar` for the type `usize` --> $DIR/method-ambig-two-traits-with-default-method.rs:6:13 | LL | trait Bar { fn method(&self) {} } | ^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `Bar::method(1_usize)` instead +help: disambiguate the method call for candidate #1 + | +LL | Foo::method(&1_usize); + | ^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | Bar::method(&1_usize); + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index c9d7da84e09f4..e7f295df8c482 100644 --- a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -25,19 +25,28 @@ note: candidate #1 is defined in an impl of the trait `internal::X` for the type | LL | fn foo(self: Smaht) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `internal::X::foo(x)` instead note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `_` --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:70:9 | LL | fn foo(self) {} | ^^^^^^^^^^^^ - = help: to disambiguate the method call, write `nuisance_foo::NuisanceFoo::foo(x)` instead note: candidate #3 is defined in the trait `FinalFoo` --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:57:5 | LL | fn foo(&self) -> u8; | ^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `FinalFoo::foo(x)` instead +help: disambiguate the method call for candidate #1 + | +LL | let z = internal::X::foo(x); + | ^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | let z = nuisance_foo::NuisanceFoo::foo(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #3 + | +LL | let z = FinalFoo::foo(x); + | ^^^^^^^^^^^^^^^^ error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:137:24 diff --git a/src/test/ui/span/issue-37767.stderr b/src/test/ui/span/issue-37767.stderr index 0bbff45436c23..9ed6c8b826f79 100644 --- a/src/test/ui/span/issue-37767.stderr +++ b/src/test/ui/span/issue-37767.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in the trait `A` | LL | fn foo(&mut self) {} | ^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `A::foo(&a)` instead note: candidate #2 is defined in the trait `B` --> $DIR/issue-37767.rs:6:5 | LL | fn foo(&mut self) {} | ^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `B::foo(&a)` instead +help: disambiguate the method call for candidate #1 + | +LL | A::foo(&a) + | ^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | B::foo(&a) + | ^^^^^^^^^^ error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:22:7 @@ -28,13 +34,19 @@ note: candidate #1 is defined in the trait `C` | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `C::foo(&a)` instead note: candidate #2 is defined in the trait `D` --> $DIR/issue-37767.rs:18:5 | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `D::foo(&a)` instead +help: disambiguate the method call for candidate #1 + | +LL | C::foo(&a) + | ^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | D::foo(&a) + | ^^^^^^^^^^ error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:34:7 @@ -47,13 +59,19 @@ note: candidate #1 is defined in the trait `E` | LL | fn foo(self) {} | ^^^^^^^^^^^^ - = help: to disambiguate the method call, write `E::foo(a)` instead note: candidate #2 is defined in the trait `F` --> $DIR/issue-37767.rs:30:5 | LL | fn foo(self) {} | ^^^^^^^^^^^^ - = help: to disambiguate the method call, write `F::foo(a)` instead +help: disambiguate the method call for candidate #1 + | +LL | E::foo(a) + | ^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | F::foo(a) + | ^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/span/issue-7575.stderr b/src/test/ui/span/issue-7575.stderr index 36db5bea86294..53a6238422b57 100644 --- a/src/test/ui/span/issue-7575.stderr +++ b/src/test/ui/span/issue-7575.stderr @@ -10,24 +10,33 @@ note: candidate #1 is defined in the trait `CtxtFn` | LL | fn f9(_: usize) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `CtxtFn::f9(u, 342)` instead note: candidate #2 is defined in the trait `OtherTrait` --> $DIR/issue-7575.rs:8:5 | LL | fn f9(_: usize) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `OtherTrait::f9(u, 342)` instead note: candidate #3 is defined in the trait `UnusedTrait` --> $DIR/issue-7575.rs:17:5 | LL | fn f9(_: usize) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `UnusedTrait::f9(u, 342)` instead = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `f9`, perhaps you need to implement one of them: candidate #1: `CtxtFn` candidate #2: `OtherTrait` candidate #3: `UnusedTrait` +help: disambiguate the method call for candidate #1 + | +LL | u.f8(42) + CtxtFn::f9(u, 342) + m.fff(42) + | ^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | u.f8(42) + OtherTrait::f9(u, 342) + m.fff(42) + | ^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #3 + | +LL | u.f8(42) + UnusedTrait::f9(u, 342) + m.fff(42) + | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0599]: no method named `fff` found for type `Myisize` in the current scope --> $DIR/issue-7575.rs:62:30 @@ -60,8 +69,11 @@ note: the candidate is defined in the trait `ManyImplTrait` | LL | fn is_str() -> bool { | ^^^^^^^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `ManyImplTrait::is_str(t)` instead = help: items from traits can only be used if the type parameter is bounded by the trait +help: disambiguate the method call for the candidate + | +LL | ManyImplTrait::is_str(t) + | help: the following trait defines an item `is_str`, perhaps you need to restrict type parameter `T` with it: | LL | fn param_bound(t: T) -> bool { diff --git a/src/test/ui/traits/trait-alias-ambiguous.stderr b/src/test/ui/traits/trait-alias-ambiguous.stderr index cde7dd0824924..48a029104aeca 100644 --- a/src/test/ui/traits/trait-alias-ambiguous.stderr +++ b/src/test/ui/traits/trait-alias-ambiguous.stderr @@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `inner::A` for the type `u | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `inner::A::foo(t)` instead note: candidate #2 is defined in an impl of the trait `inner::B` for the type `u8` --> $DIR/trait-alias-ambiguous.rs:11:9 | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ - = help: to disambiguate the method call, write `inner::B::foo(t)` instead +help: disambiguate the method call for candidate #1 + | +LL | inner::A::foo(&t); + | ^^^^^^^^^^^^^^^^^ +help: disambiguate the method call for candidate #2 + | +LL | inner::B::foo(&t); + | ^^^^^^^^^^^^^^^^^ error: aborting due to previous error From 8c4f1d5f875e29e9a6e063744b71dc0a3c7deb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 11 Dec 2019 18:20:29 -0800 Subject: [PATCH 12/13] review comments --- src/librustc_typeck/check/method/suggest.rs | 134 ++++++++++-------- src/test/ui/error-codes/E0034.stderr | 8 +- ...method-ambig-two-traits-from-impls2.stderr | 8 +- 3 files changed, 84 insertions(+), 66 deletions(-) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index cd26e6f237c7d..9cd8c9abfd783 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -15,7 +15,7 @@ use rustc::traits::Obligation; use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable}; use rustc::ty::print::with_crate_prefix; use syntax_pos::{Span, FileName}; -use syntax::ast; +use syntax::{ast, source_map}; use syntax::util::lev_distance; use rustc_error_codes::*; @@ -79,61 +79,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - let print_disambiguation_help = | - err: &mut DiagnosticBuilder<'_>, - trait_name: String, - rcvr_ty: Ty<'_>, - kind: ty::AssocKind, - span: Span, - candidate: Option, - | { - let mut applicability = Applicability::MachineApplicable; - let sugg_args = if let ty::AssocKind::Method = kind { - format!( - "({}{})", - if rcvr_ty.is_region_ptr() && args.is_some() { - if rcvr_ty.is_mutable_ptr() { - "&mut " - } else { - "&" - } - } else { - "" - }, - args.map(|arg| arg - .iter() - .map(|arg| self.tcx.sess.source_map().span_to_snippet(arg.span) - .unwrap_or_else(|_| { - applicability = Applicability::HasPlaceholders; - "...".to_owned() - })) - .collect::>() - .join(", ") - ).unwrap_or_else(|| { - applicability = Applicability::HasPlaceholders; - "...".to_owned() - }), - ) - } else { - String::new() - }; - let sugg = format!("{}::{}{}", trait_name, item_name, sugg_args); - err.span_suggestion( - span, - &format!( - "disambiguate the {} for {}", - kind.suggestion_descr(), - if let Some(candidate) = candidate { - format!("candidate #{}", candidate) - } else { - "the candidate".to_string() - }, - ), - sugg, - applicability, - ); - }; - let report_candidates = | span: Span, err: &mut DiagnosticBuilder<'_>, @@ -215,7 +160,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|ty| *ty) .unwrap_or(rcvr_ty), }; - print_disambiguation_help(err, path, ty, item.kind, sugg_span, idx); + print_disambiguation_help( + item_name, + args, + err, + path, + ty, + item.kind, + sugg_span, + idx, + self.tcx.sess.source_map(), + ); } } CandidateSource::TraitSource(trait_did) => { @@ -244,7 +199,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; let path = self.tcx.def_path_str(trait_did); - print_disambiguation_help(err, path, rcvr_ty, item.kind, sugg_span, idx); + print_disambiguation_help( + item_name, + args, + err, + path, + rcvr_ty, + item.kind, + sugg_span, + idx, + self.tcx.sess.source_map(), + ); } } } @@ -1180,3 +1145,56 @@ impl hir::intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> { hir::intravisit::NestedVisitorMap::None } } + +fn print_disambiguation_help( + item_name: ast::Ident, + args: Option<&'tcx [hir::Expr]>, + err: &mut DiagnosticBuilder<'_>, + trait_name: String, + rcvr_ty: Ty<'_>, + kind: ty::AssocKind, + span: Span, + candidate: Option, + source_map: &source_map::SourceMap, +) { + let mut applicability = Applicability::MachineApplicable; + let sugg_args = if let (ty::AssocKind::Method, Some(args)) = (kind, args) { + format!( + "({}{})", + if rcvr_ty.is_region_ptr() { + if rcvr_ty.is_mutable_ptr() { + "&mut " + } else { + "&" + } + } else { + "" + }, + args.iter() + .map(|arg| source_map.span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::>() + .join(", "), + ) + } else { + String::new() + }; + let sugg = format!("{}::{}{}", trait_name, item_name, sugg_args); + err.span_suggestion( + span, + &format!( + "disambiguate the {} for {}", + kind.suggestion_descr(), + if let Some(candidate) = candidate { + format!("candidate #{}", candidate) + } else { + "the candidate".to_string() + }, + ), + sugg, + applicability, + ); +} diff --git a/src/test/ui/error-codes/E0034.stderr b/src/test/ui/error-codes/E0034.stderr index 30b44fd402bb8..6db2ef5051d83 100644 --- a/src/test/ui/error-codes/E0034.stderr +++ b/src/test/ui/error-codes/E0034.stderr @@ -16,12 +16,12 @@ LL | fn foo() {} | ^^^^^^^^ help: disambiguate the method call for candidate #1 | -LL | Trait1::foo(...)() - | ^^^^^^^^^^^^^^^^ +LL | Trait1::foo() + | ^^^^^^^^^^^ help: disambiguate the method call for candidate #2 | -LL | Trait2::foo(...)() - | ^^^^^^^^^^^^^^^^ +LL | Trait2::foo() + | ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr index 55499215799d7..44f85071505d2 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr @@ -16,12 +16,12 @@ LL | fn foo() {} | ^^^^^^^^ help: disambiguate the method call for candidate #1 | -LL | A::foo(...)(); - | ^^^^^^^^^^^ +LL | A::foo(); + | ^^^^^^ help: disambiguate the method call for candidate #2 | -LL | B::foo(...)(); - | ^^^^^^^^^^^ +LL | B::foo(); + | ^^^^^^ error: aborting due to previous error From 985127ce8fe6ec1351768224579ccc566689ff39 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 24 Nov 2019 20:44:19 +0000 Subject: [PATCH 13/13] Implement `finish_non_exhaustive` for `DebugStruct`. --- src/libcore/fmt/builders.rs | 69 ++++++++++++++++++++++--- src/libcore/tests/fmt/builders.rs | 83 +++++++++++++++++++++++++++++++ src/libcore/tests/lib.rs | 1 + 3 files changed, 147 insertions(+), 6 deletions(-) diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index e6e3454b36f81..dcdd26634de46 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs @@ -165,6 +165,62 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { self } + /// Marks the struct as non-exhaustive, indicating to the reader that there are some other + /// fields that are not shown in the debug representation. + /// + /// # Examples + /// + /// ``` + /// # #![feature(debug_non_exhaustive)] + /// use std::fmt; + /// + /// struct Bar { + /// bar: i32, + /// hidden: f32, + /// } + /// + /// impl fmt::Debug for Bar { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// fmt.debug_struct("Bar") + /// .field("bar", &self.bar) + /// .finish_non_exhaustive() // Show that some other field(s) exist. + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Bar { bar: 10, hidden: 1.0 }), + /// "Bar { bar: 10, .. }", + /// ); + /// ``` + #[unstable(feature = "debug_non_exhaustive", issue = "67364")] + pub fn finish_non_exhaustive(&mut self) -> fmt::Result { + self.result = self.result.and_then(|_| { + // Draw non-exhaustive dots (`..`), and open brace if necessary (no fields). + if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str(" {\n")?; + } + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + } else { + if self.has_fields { + self.fmt.write_str(", ..")?; + } else { + self.fmt.write_str(" { ..")?; + } + } + if self.is_pretty() { + self.fmt.write_str("}")? + } else { + self.fmt.write_str(" }")?; + } + Ok(()) + }); + self.result + } + /// Finishes output and returns any error encountered. /// /// # Examples @@ -194,15 +250,16 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - if self.has_fields { - self.result = self.result.and_then(|_| { + self.result = self.result.and_then(|_| { + if self.has_fields { if self.is_pretty() { - self.fmt.write_str("}") + self.fmt.write_str("}")? } else { - self.fmt.write_str(" }") + self.fmt.write_str(" }")?; } - }); - } + } + Ok(()) + }); self.result } diff --git a/src/libcore/tests/fmt/builders.rs b/src/libcore/tests/fmt/builders.rs index 255724432816d..8f426abe43ffa 100644 --- a/src/libcore/tests/fmt/builders.rs +++ b/src/libcore/tests/fmt/builders.rs @@ -93,6 +93,89 @@ mod debug_struct { }", format!("{:#?}", Bar)); } + + #[test] + fn test_only_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Foo") + .finish_non_exhaustive() + } + } + + + assert_eq!("Foo { .. }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + .. +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_multiple_and_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Foo") + .field("bar", &true) + .field("baz", &format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + assert_eq!("Foo { bar: true, baz: 10/20, .. }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + bar: true, + baz: 10/20, + .. +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_nested_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Foo") + .field("bar", &true) + .field("baz", &format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Bar") + .field("foo", &Foo) + .field("hello", &"world") + .finish_non_exhaustive() + } + } + + assert_eq!("Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: \"world\", .. }", + format!("{:?}", Bar)); + assert_eq!( +"Bar { + foo: Foo { + bar: true, + baz: 10/20, + .. + }, + hello: \"world\", + .. +}", + format!("{:#?}", Bar)); + } + } mod debug_tuple { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index b28ed2eaa0876..904c37fe54255 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -5,6 +5,7 @@ #![feature(core_private_bignum)] #![feature(core_private_diy_float)] #![feature(debug_map_key_value)] +#![feature(debug_non_exhaustive)] #![feature(dec2flt)] #![feature(exact_size_is_empty)] #![feature(fixed_size_array)]