From a5732fdc93af4a6a694ef58e75399c2cc89762d0 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 21 Nov 2022 03:28:16 +0200 Subject: [PATCH 01/67] reflow the stack size story --- library/std/src/thread/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 34bdb8bd4612..9ef65e24128c 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -124,8 +124,10 @@ //! //! ## Stack size //! -//! The default stack size is platform-dependent and subject to change. Currently it is 2MB on all -//! Tier-1 platforms. There are two ways to manually specify the stack size for spawned threads: +//! The default stack size is platform-dependent and subject to change. +//! Currently, it is 2 MiB on all Tier-1 platforms. +//! +//! There are two ways to manually specify the stack size for spawned threads: //! //! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`]. //! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack From 7c13b145f47e36158c252e0359a8374f1aff3e5e Mon Sep 17 00:00:00 2001 From: Markus Everling Date: Thu, 29 Dec 2022 02:22:40 +0100 Subject: [PATCH 02/67] Implement more methods for `vec_deque::IntoIter` --- .../src/collections/vec_deque/into_iter.rs | 185 +++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index e54880e86523..34bc0ce9177c 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -1,5 +1,5 @@ -use core::fmt; use core::iter::{FusedIterator, TrustedLen}; +use core::{array, fmt, mem::MaybeUninit, ops::Try, ptr}; use crate::alloc::{Allocator, Global}; @@ -52,6 +52,126 @@ impl Iterator for IntoIter { let len = self.inner.len(); (len, Some(len)) } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + if self.inner.len < n { + let len = self.inner.len; + self.inner.clear(); + Err(len) + } else { + self.inner.drain(..n); + Ok(()) + } + } + + #[inline] + fn count(self) -> usize { + self.inner.len + } + + fn try_fold(&mut self, mut init: B, mut f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + struct Guard<'a, T, A: Allocator> { + deque: &'a mut VecDeque, + // `consumed <= deque.len` always holds. + consumed: usize, + } + + impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { + fn drop(&mut self) { + self.deque.len -= self.consumed; + self.deque.head = self.deque.to_physical_idx(self.consumed); + } + } + + let mut guard = Guard { deque: &mut self.inner, consumed: 0 }; + + let (head, tail) = guard.deque.as_slices(); + + init = head + .iter() + .map(|elem| { + guard.consumed += 1; + // SAFETY: Because we incremented `guard.consumed`, the + // deque effectively forgot the element, so we can take + // ownership + unsafe { ptr::read(elem) } + }) + .try_fold(init, &mut f)?; + + tail.iter() + .map(|elem| { + guard.consumed += 1; + // SAFETY: Same as above. + unsafe { ptr::read(elem) } + }) + .try_fold(init, &mut f) + } + + #[inline] + fn fold(mut self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + match self.try_fold(init, |b, item| Ok::(f(b, item))) { + Ok(b) => b, + Err(e) => match e {}, + } + } + + #[inline] + fn last(mut self) -> Option { + self.inner.pop_back() + } + + fn next_chunk( + &mut self, + ) -> Result<[Self::Item; N], array::IntoIter> { + let mut raw_arr = MaybeUninit::uninit_array(); + let raw_arr_ptr = raw_arr.as_mut_ptr().cast(); + let (head, tail) = self.inner.as_slices(); + + if head.len() >= N { + // SAFETY: By manually adjusting the head and length of the deque, we effectively + // make it forget the first `N` elements, so taking ownership of them is safe. + unsafe { ptr::copy_nonoverlapping(head.as_ptr(), raw_arr_ptr, N) }; + self.inner.head = self.inner.to_physical_idx(N); + self.inner.len -= N; + // SAFETY: We initialized the entire array with items from `head` + return Ok(unsafe { raw_arr.transpose().assume_init() }); + } + + // SAFETY: Same argument as above. + unsafe { ptr::copy_nonoverlapping(head.as_ptr(), raw_arr_ptr, head.len()) }; + let remaining = N - head.len(); + + if tail.len() >= remaining { + // SAFETY: Same argument as above. + unsafe { + ptr::copy_nonoverlapping(tail.as_ptr(), raw_arr_ptr.add(head.len()), remaining) + }; + self.inner.head = self.inner.to_physical_idx(N); + self.inner.len -= N; + // SAFETY: We initialized the entire array with items from `head` and `tail` + Ok(unsafe { raw_arr.transpose().assume_init() }) + } else { + // SAFETY: Same argument as above. + unsafe { + ptr::copy_nonoverlapping(tail.as_ptr(), raw_arr_ptr.add(head.len()), tail.len()) + }; + let init = head.len() + tail.len(); + // We completely drained all the deques elements. + self.inner.head = 0; + self.inner.len = 0; + // SAFETY: We copied all elements from both slices to the beginning of the array, so + // the given range is initialized. + Err(unsafe { array::IntoIter::new_unchecked(raw_arr, 0..init) }) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -60,10 +180,73 @@ impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { self.inner.pop_back() } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + let len = self.inner.len; + if len >= n { + self.inner.truncate(len - n); + Ok(()) + } else { + self.inner.clear(); + Err(len) + } + } + + fn try_rfold(&mut self, mut init: B, mut f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + struct Guard<'a, T, A: Allocator> { + deque: &'a mut VecDeque, + // `consumed <= deque.len` always holds. + consumed: usize, + } + + impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { + fn drop(&mut self) { + self.deque.len -= self.consumed; + } + } + + let mut guard = Guard { deque: &mut self.inner, consumed: 0 }; + + let (head, tail) = guard.deque.as_slices(); + + init = tail + .iter() + .map(|elem| { + guard.consumed += 1; + // SAFETY: See `try_fold`'s safety comment. + unsafe { ptr::read(elem) } + }) + .try_rfold(init, &mut f)?; + + head.iter() + .map(|elem| { + guard.consumed += 1; + // SAFETY: Same as above. + unsafe { ptr::read(elem) } + }) + .try_rfold(init, &mut f) + } + + #[inline] + fn rfold(mut self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + match self.try_rfold(init, |b, item| Ok::(f(b, item))) { + Ok(b) => b, + Err(e) => match e {}, + } + } } #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter { + #[inline] fn is_empty(&self) -> bool { self.inner.is_empty() } From ccba6c51511a86cd36af52dc4f51cd3a0df95909 Mon Sep 17 00:00:00 2001 From: Markus Everling Date: Tue, 17 Jan 2023 21:08:23 +0100 Subject: [PATCH 03/67] Add `vec_deque::IntoIter` benchmarks --- library/alloc/benches/vec_deque.rs | 146 ++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/library/alloc/benches/vec_deque.rs b/library/alloc/benches/vec_deque.rs index 7c78561ebf10..313a97ed1ffc 100644 --- a/library/alloc/benches/vec_deque.rs +++ b/library/alloc/benches/vec_deque.rs @@ -1,4 +1,8 @@ -use std::collections::VecDeque; +use core::iter::Iterator; +use std::{ + collections::{vec_deque, VecDeque}, + mem, +}; use test::{black_box, Bencher}; #[bench] @@ -53,6 +57,146 @@ fn bench_try_fold(b: &mut Bencher) { b.iter(|| black_box(ring.iter().try_fold(0, |a, b| Some(a + b)))) } +/// does the memory bookkeeping to reuse the buffer of the Vec between iterations. +/// `setup` must not modify its argument's length or capacity. `g` must not move out of its argument. +fn into_iter_helper< + T: Copy, + F: FnOnce(&mut VecDeque), + G: FnOnce(&mut vec_deque::IntoIter), +>( + v: &mut Vec, + setup: F, + g: G, +) { + let ptr = v.as_mut_ptr(); + let len = v.len(); + // ensure that the vec is full, to make sure that any wrapping from the deque doesn't + // access uninitialized memory. + assert_eq!(v.len(), v.capacity()); + + let mut deque = VecDeque::from(mem::take(v)); + setup(&mut deque); + + let mut it = deque.into_iter(); + g(&mut it); + + mem::forget(it); + + // SAFETY: the provided functions are not allowed to modify the allocation, so the buffer is still alive. + // len and capacity are accurate due to the above assertion. + // All the elements in the buffer are still valid, because of `T: Copy` which implies `T: !Drop`. + mem::forget(mem::replace(v, unsafe { Vec::from_raw_parts(ptr, len, len) })); +} + +#[bench] +fn bench_into_iter(b: &mut Bencher) { + let len = 1024; + // we reuse this allocation for every run + let mut vec: Vec = (0..len).collect(); + vec.shrink_to_fit(); + + b.iter(|| { + let mut sum = 0; + into_iter_helper( + &mut vec, + |_| {}, + |it| { + for i in it { + sum += i; + } + }, + ); + black_box(sum); + + let mut sum = 0; + // rotating a full deque doesn't move any memory. + into_iter_helper( + &mut vec, + |d| d.rotate_left(len / 2), + |it| { + for i in it { + sum += i; + } + }, + ); + black_box(sum); + }); +} + +#[bench] +fn bench_into_iter_fold(b: &mut Bencher) { + let len = 1024; + + // because `fold` takes ownership of the iterator, + // we can't prevent it from dropping the memory, + // so we have to bite the bullet and reallocate + // for every iteration. + b.iter(|| { + let deque: VecDeque = (0..len).collect(); + assert_eq!(deque.len(), deque.capacity()); + let sum = deque.into_iter().fold(0, |a, b| a + b); + black_box(sum); + + // rotating a full deque doesn't move any memory. + let mut deque: VecDeque = (0..len).collect(); + assert_eq!(deque.len(), deque.capacity()); + deque.rotate_left(len / 2); + let sum = deque.into_iter().fold(0, |a, b| a + b); + black_box(sum); + }); +} + +#[bench] +fn bench_into_iter_try_fold(b: &mut Bencher) { + let len = 1024; + // we reuse this allocation for every run + let mut vec: Vec = (0..len).collect(); + vec.shrink_to_fit(); + + // Iterator::any uses Iterator::try_fold under the hood + b.iter(|| { + let mut b = false; + into_iter_helper(&mut vec, |_| {}, |it| b = it.any(|i| i == len - 1)); + black_box(b); + + into_iter_helper(&mut vec, |d| d.rotate_left(len / 2), |it| b = it.any(|i| i == len - 1)); + black_box(b); + }); +} + +#[bench] +fn bench_into_iter_next_chunk(b: &mut Bencher) { + let len = 1024; + // we reuse this allocation for every run + let mut vec: Vec = (0..len).collect(); + vec.shrink_to_fit(); + + b.iter(|| { + let mut buf = [0; 64]; + into_iter_helper( + &mut vec, + |_| {}, + |it| { + while let Ok(a) = it.next_chunk() { + buf = a; + } + }, + ); + black_box(buf); + + into_iter_helper( + &mut vec, + |d| d.rotate_left(len / 2), + |it| { + while let Ok(a) = it.next_chunk() { + buf = a; + } + }, + ); + black_box(buf); + }); +} + #[bench] fn bench_from_array_1000(b: &mut Bencher) { const N: usize = 1000; From bbf33836b9adfe4328aefa108c421e670a3923b7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 31 Jan 2023 20:37:52 +0000 Subject: [PATCH 04/67] Fingerprint even when incr comp is disabled in debug mode --- compiler/rustc_query_system/src/lib.rs | 1 + .../rustc_query_system/src/query/plumbing.rs | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index f47760e9ae6c..623be668464e 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -3,6 +3,7 @@ #![feature(hash_raw_entry)] #![feature(min_specialization)] #![feature(extern_types)] +#![feature(let_chains)] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index b3b939eae88d..1f1bb36aacea 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -2,7 +2,7 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. -use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex}; +use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams}; use crate::ich::StableHashingContext; use crate::query::caches::QueryCache; use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; @@ -428,12 +428,29 @@ where // Fast path for when incr. comp. is off. if !dep_graph.is_fully_enabled() { + // Fingerprint the key, just to assert that it doesn't + // have anything we don't consider hashable + if cfg!(debug_assertions) { + let _ = key.to_fingerprint(*qcx.dep_context()); + } + let prof_timer = qcx.dep_context().profiler().query_provider(); let result = qcx.start_query(job_id, Q::DEPTH_LIMIT, None, || { Q::compute(qcx, &key)(*qcx.dep_context(), key) }); let dep_node_index = dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + // Similarly, fingerprint the result to assert that + // it doesn't have anything not considered hashable. + if cfg!(debug_assertions) + && let Some(hash_result) = Q::HASH_RESULT + { + qcx.dep_context().with_stable_hashing_context(|mut hcx| { + hash_result(&mut hcx, &result); + }); + } + return (result, dep_node_index); } From 789e8283dd7305c95b65672bb21712a69b31e677 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 1 Feb 2023 01:12:57 +0000 Subject: [PATCH 05/67] Don't call with_reveal_all_normalized in evaluate when param-env has inference vars in it --- compiler/rustc_middle/src/ty/consts/kind.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index d9721863a58c..b0593f1f887f 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -217,23 +217,21 @@ impl<'tcx> ConstKind<'tcx> { // Note that we erase regions *before* calling `with_reveal_all_normalized`, // so that we don't try to invoke this query with // any region variables. - let param_env_and = tcx - .erase_regions(param_env) - .with_reveal_all_normalized(tcx) - .and(tcx.erase_regions(unevaluated)); // HACK(eddyb) when the query key would contain inference variables, // attempt using identity substs and `ParamEnv` instead, that will succeed // when the expression doesn't depend on any parameters. // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that // we can call `infcx.const_eval_resolve` which handles inference variables. - let param_env_and = if param_env_and.needs_infer() { + let param_env_and = if (param_env, unevaluated).has_non_region_infer() { tcx.param_env(unevaluated.def.did).and(ty::UnevaluatedConst { def: unevaluated.def, substs: InternalSubsts::identity_for_item(tcx, unevaluated.def.did), }) } else { - param_env_and + tcx.erase_regions(param_env) + .with_reveal_all_normalized(tcx) + .and(tcx.erase_regions(unevaluated)) }; // FIXME(eddyb) maybe the `const_eval_*` methods should take From 894c98652c91a5ff824e81d847760c518857d0a2 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 7 Feb 2023 19:00:18 -0700 Subject: [PATCH 06/67] rustdoc: simplify DOM for `.item-table` This switches from using `
` to the more semantic `
    `, and using class names that rhyme with the classes the search results table uses. --- src/librustdoc/html/render/print_item.rs | 20 +++++------ src/librustdoc/html/static/css/rustdoc.css | 16 +++++---- .../huge-collection-of-constants.goml | 4 +-- tests/rustdoc-gui/item-summary-table.goml | 4 +-- tests/rustdoc-gui/label-next-to-symbol.goml | 36 +++++++++---------- tests/rustdoc-gui/module-items-font.goml | 26 +++++++------- tests/rustdoc-gui/sidebar.goml | 10 +++--- tests/rustdoc-gui/unsafe-fn.goml | 2 +- tests/rustdoc/cfg_doc_reexport.rs | 4 +-- tests/rustdoc/deprecated.rs | 6 ++-- tests/rustdoc/doc-cfg.rs | 6 ++-- tests/rustdoc/duplicate-cfg.rs | 4 +-- tests/rustdoc/glob-shadowing-const.rs | 4 +-- tests/rustdoc/glob-shadowing.rs | 18 +++++----- tests/rustdoc/inline_cross/macros.rs | 4 +-- tests/rustdoc/internal.rs | 6 ++-- tests/rustdoc/issue-32374.rs | 6 ++-- tests/rustdoc/issue-46377.rs | 2 +- tests/rustdoc/issue-55364.rs | 8 ++--- tests/rustdoc/issue-95873.rs | 2 +- tests/rustdoc/reexport-check.rs | 4 +-- tests/rustdoc/short-docblock-codeblock.rs | 2 +- tests/rustdoc/short-docblock.rs | 10 +++--- 23 files changed, 103 insertions(+), 101 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index c2d24e514845..f79a017997c2 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -39,10 +39,10 @@ use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; -const ITEM_TABLE_OPEN: &str = "
    "; -const ITEM_TABLE_CLOSE: &str = "
    "; -const ITEM_TABLE_ROW_OPEN: &str = "
    "; -const ITEM_TABLE_ROW_CLOSE: &str = "
    "; +const ITEM_TABLE_OPEN: &str = "
      "; +const ITEM_TABLE_CLOSE: &str = "
    "; +const ITEM_TABLE_ROW_OPEN: &str = "
  • "; +const ITEM_TABLE_ROW_CLOSE: &str = "
  • "; // A component in a `use` path, like `string` in std::string::ToString struct PathComponent { @@ -338,14 +338,14 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: match *src { Some(src) => write!( w, - "
    {}extern crate {} as {};", + "
    {}extern crate {} as {};", visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), anchor(myitem.item_id.expect_def_id(), src, cx), myitem.name.unwrap(), ), None => write!( w, - "
    {}extern crate {};", + "
    {}extern crate {};", visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx), ), @@ -384,11 +384,11 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() { ("", "") } else { - ("
    ", "
    ") + ("
    ", "
    ") }; write!( w, - "
    \ + "
    \ {vis}{imp}\
    \ {stab_tags_before}{stab_tags}{stab_tags_after}", @@ -426,11 +426,11 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: let (docs_before, docs_after) = if docs.is_empty() { ("", "") } else { - ("
    ", "
    ") + ("
    ", "
    ") }; write!( w, - "
    \ + "
    \ {name}\ {visibility_emoji}\ {unsafety_flag}\ diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 2a9548712f08..ce7bd95fde32 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -193,7 +193,7 @@ h1, h2, h3, h4, h5, h6, .mobile-topbar, .search-input, .search-results .result-name, -.item-left > a, +.item-name > a, .out-of-band, span.since, a.srclink, @@ -742,14 +742,16 @@ table, .item-table { display: table; + padding: 0; + margin: 0; } -.item-row { +.item-table > li { display: table-row; } -.item-left, .item-right { +.item-table > li > div { display: table-cell; } -.item-left { +.item-table > li > .item-name { padding-right: 1.25rem; } @@ -954,7 +956,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ padding: 3px; margin-bottom: 5px; } -.item-left .stab { +.item-name .stab { margin-left: 0.3125em; } .stab { @@ -1723,7 +1725,7 @@ in storage.js } /* Display an alternating layout on tablets and phones */ - .item-table, .item-row, .item-left, .item-right, + .item-table, .item-row, .item-table > li, .item-table > li > div, .search-results > a, .search-results > a > div { display: block; } @@ -1732,7 +1734,7 @@ in storage.js .search-results > a { padding: 5px 0px; } - .search-results > a > div.desc, .item-right { + .search-results > a > div.desc, .item-table > li > div.desc { padding-left: 2em; } diff --git a/tests/rustdoc-gui/huge-collection-of-constants.goml b/tests/rustdoc-gui/huge-collection-of-constants.goml index 3ccd33f1ccda..636382a91699 100644 --- a/tests/rustdoc-gui/huge-collection-of-constants.goml +++ b/tests/rustdoc-gui/huge-collection-of-constants.goml @@ -3,7 +3,7 @@ goto: "file://" + |DOC_PATH| + "/test_docs/huge_amount_of_consts/index.html" compare-elements-position-near-false: ( - "//*[@class='item-table']//div[last()-1]", - "//*[@class='item-table']//div[last()-3]", + "//ul[@class='item-table']/li[last()-1]", + "//ul[@class='item-table']/li[last()-3]", {"y": 12}, ) diff --git a/tests/rustdoc-gui/item-summary-table.goml b/tests/rustdoc-gui/item-summary-table.goml index 2a92e9da52ce..7a219bd54c3e 100644 --- a/tests/rustdoc-gui/item-summary-table.goml +++ b/tests/rustdoc-gui/item-summary-table.goml @@ -1,6 +1,6 @@ // This test ensures that elements aren't display in items summary. goto: "file://" + |DOC_PATH| + "/lib2/summary_table/index.html" // We check that we picked the right item first. -assert-text: (".item-table .item-left", "Foo") +assert-text: (".item-table .item-name", "Foo") // Then we check that its summary is empty. -assert-false: ".item-table .item-right" +assert-false: ".item-table .desc" diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml index 3f4f65890b42..412e475dcc9b 100644 --- a/tests/rustdoc-gui/label-next-to-symbol.goml +++ b/tests/rustdoc-gui/label-next-to-symbol.goml @@ -9,31 +9,31 @@ assert: (".stab.portability") // make sure that deprecated and portability have the right colors assert-css: ( - ".item-table .item-left .stab.deprecated", + ".item-table .item-name .stab.deprecated", { "background-color": "rgb(255, 245, 214)" }, ) assert-css: ( - ".item-table .item-left .stab.portability", + ".item-table .item-name .stab.portability", { "background-color": "rgb(255, 245, 214)" }, ) // table like view -assert-css: (".item-right.docblock-short", { "padding-left": "0px" }) +assert-css: (".desc.docblock-short", { "padding-left": "0px" }) compare-elements-position-near: ( - "//*[@class='item-left']//a[text()='replaced_function']", - ".item-left .stab.deprecated", + "//*[@class='item-name']//a[text()='replaced_function']", + ".item-name .stab.deprecated", {"y": 2}, ) compare-elements-position: ( - ".item-left .stab.deprecated", - ".item-left .stab.portability", + ".item-name .stab.deprecated", + ".item-name .stab.portability", ("y"), ) // Ensure no wrap compare-elements-position: ( - "//*[@class='item-left']//a[text()='replaced_function']/..", - "//*[@class='item-right docblock-short'][text()='a thing with a label']", + "//*[@class='item-name']//a[text()='replaced_function']/..", + "//*[@class='desc docblock-short'][text()='a thing with a label']", ("y"), ) @@ -41,26 +41,26 @@ compare-elements-position: ( // Mobile view size: (600, 600) // staggered layout with 2em spacing -assert-css: (".item-right.docblock-short", { "padding-left": "32px" }) +assert-css: (".desc.docblock-short", { "padding-left": "32px" }) compare-elements-position-near: ( - "//*[@class='item-left']//a[text()='replaced_function']", - ".item-left .stab.deprecated", + "//*[@class='item-name']//a[text()='replaced_function']", + ".item-name .stab.deprecated", {"y": 2}, ) compare-elements-position: ( - ".item-left .stab.deprecated", - ".item-left .stab.portability", + ".item-name .stab.deprecated", + ".item-name .stab.portability", ("y"), ) // Ensure wrap compare-elements-position-false: ( - "//*[@class='item-left']//a[text()='replaced_function']/..", - "//*[@class='item-right docblock-short'][text()='a thing with a label']", + "//*[@class='item-name']//a[text()='replaced_function']/..", + "//*[@class='desc docblock-short'][text()='a thing with a label']", ("y"), ) compare-elements-position-false: ( - ".item-left .stab.deprecated", - "//*[@class='item-right docblock-short'][text()='a thing with a label']", + ".item-name .stab.deprecated", + "//*[@class='desc docblock-short'][text()='a thing with a label']", ("y"), ) diff --git a/tests/rustdoc-gui/module-items-font.goml b/tests/rustdoc-gui/module-items-font.goml index 5940962a8ddb..23823f8b6c79 100644 --- a/tests/rustdoc-gui/module-items-font.goml +++ b/tests/rustdoc-gui/module-items-font.goml @@ -1,7 +1,7 @@ // This test checks that the correct font is used on module items (in index.html pages). goto: "file://" + |DOC_PATH| + "/test_docs/index.html" assert-css: ( - ".item-table .item-left > a", + ".item-table .item-name > a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ALL, ) @@ -13,55 +13,55 @@ assert-css: ( // modules assert-css: ( - "#modules + .item-table .item-left a", + "#modules + .item-table .item-name a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ) assert-css: ( - "#modules + .item-table .item-right.docblock-short", + "#modules + .item-table .desc.docblock-short", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ) // structs assert-css: ( - "#structs + .item-table .item-left a", + "#structs + .item-table .item-name a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ) assert-css: ( - "#structs + .item-table .item-right.docblock-short", + "#structs + .item-table .desc.docblock-short", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ) // enums assert-css: ( - "#enums + .item-table .item-left a", + "#enums + .item-table .item-name a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ) assert-css: ( - "#enums + .item-table .item-right.docblock-short", + "#enums + .item-table .desc.docblock-short", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ) // traits assert-css: ( - "#traits + .item-table .item-left a", + "#traits + .item-table .item-name a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ) assert-css: ( - "#traits + .item-table .item-right.docblock-short", + "#traits + .item-table .desc.docblock-short", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ) // functions assert-css: ( - "#functions + .item-table .item-left a", + "#functions + .item-table .item-name a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ) assert-css: ( - "#functions + .item-table .item-right.docblock-short", + "#functions + .item-table .desc.docblock-short", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ) // keywords assert-css: ( - "#keywords + .item-table .item-left a", + "#keywords + .item-table .item-name a", {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'}, ) assert-css: ( - "#keywords + .item-table .item-right.docblock-short", + "#keywords + .item-table .desc.docblock-short", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ) diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 9c742be0587c..a6d51709019e 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -70,8 +70,8 @@ assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Functions") assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Type Definitions") assert-text: (".sidebar-elems section ul > li:nth-child(10)", "Unions") assert-text: (".sidebar-elems section ul > li:nth-child(11)", "Keywords") -assert-text: ("#structs + .item-table .item-left > a", "Foo") -click: "#structs + .item-table .item-left > a" +assert-text: ("#structs + .item-table .item-name > a", "Foo") +click: "#structs + .item-table .item-name > a" // PAGE: struct.Foo.html assert-count: (".sidebar .location", 1) @@ -103,8 +103,8 @@ assert-text: (".sidebar-elems > section ul.block > li:nth-child(2)", "Structs") assert-text: (".sidebar-elems > section ul.block > li:nth-child(3)", "Traits") assert-text: (".sidebar-elems > section ul.block > li:nth-child(4)", "Functions") assert-text: (".sidebar-elems > section ul.block > li:nth-child(5)", "Type Definitions") -assert-text: ("#functions + .item-table .item-left > a", "foobar") -click: "#functions + .item-table .item-left > a" +assert-text: ("#functions + .item-table .item-name > a", "foobar") +click: "#functions + .item-table .item-name > a" // PAGE: fn.foobar.html // In items containing no items (like functions or constants) and in modules, we have no @@ -127,7 +127,7 @@ assert-text: (".sidebar > .location", "Module sub_sub_module") // We check that we don't have the crate list. assert-false: ".sidebar-elems .crate" assert-text: (".sidebar-elems > section ul > li:nth-child(1)", "Functions") -assert-text: ("#functions + .item-table .item-left > a", "foo") +assert-text: ("#functions + .item-table .item-name > a", "foo") // Links to trait implementations in the sidebar should not wrap even if they are long. goto: "file://" + |DOC_PATH| + "/lib2/struct.HasALongTraitWithParams.html" diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml index 3ecb25c82a44..9d2577178c08 100644 --- a/tests/rustdoc-gui/unsafe-fn.goml +++ b/tests/rustdoc-gui/unsafe-fn.goml @@ -19,7 +19,7 @@ define-function: ( local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} // We reload the page so the local storage settings are being used. reload: - assert-css: (".item-left sup", {"color": |color|}) + assert-css: (".item-name sup", {"color": |color|}) }, ) diff --git a/tests/rustdoc/cfg_doc_reexport.rs b/tests/rustdoc/cfg_doc_reexport.rs index 89c7f0a6f342..a10c84f2cacc 100644 --- a/tests/rustdoc/cfg_doc_reexport.rs +++ b/tests/rustdoc/cfg_doc_reexport.rs @@ -5,8 +5,8 @@ #![no_core] // @has 'foo/index.html' -// @has - '//*[@class="item-left"]/*[@class="stab portability"]' 'foobar' -// @has - '//*[@class="item-left"]/*[@class="stab portability"]' 'bar' +// @has - '//*[@class="item-name"]/*[@class="stab portability"]' 'foobar' +// @has - '//*[@class="item-name"]/*[@class="stab portability"]' 'bar' #[doc(cfg(feature = "foobar"))] mod imp_priv { diff --git a/tests/rustdoc/deprecated.rs b/tests/rustdoc/deprecated.rs index 5cbe4d59108a..51860441b359 100644 --- a/tests/rustdoc/deprecated.rs +++ b/tests/rustdoc/deprecated.rs @@ -1,6 +1,6 @@ -// @has deprecated/index.html '//*[@class="item-left"]/span[@class="stab deprecated"]' \ +// @has deprecated/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \ // 'Deprecated' -// @has - '//*[@class="item-right docblock-short"]' 'Deprecated docs' +// @has - '//*[@class="desc docblock-short"]' 'Deprecated docs' // @has deprecated/struct.S.html '//*[@class="stab deprecated"]' \ // 'Deprecated since 1.0.0: text' @@ -8,7 +8,7 @@ #[deprecated(since = "1.0.0", note = "text")] pub struct S; -// @matches deprecated/index.html '//*[@class="item-right docblock-short"]' '^Docs' +// @matches deprecated/index.html '//*[@class="desc docblock-short"]' '^Docs' /// Docs pub struct T; diff --git a/tests/rustdoc/doc-cfg.rs b/tests/rustdoc/doc-cfg.rs index 1cfbfec6fcd3..c4702d4109e3 100644 --- a/tests/rustdoc/doc-cfg.rs +++ b/tests/rustdoc/doc-cfg.rs @@ -12,7 +12,7 @@ pub struct Portable; // @has doc_cfg/unix_only/index.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ // 'Available on Unix only.' -// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\AARM\Z' +// @matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AARM\Z' // @count - '//*[@class="stab portability"]' 2 #[doc(cfg(unix))] pub mod unix_only { @@ -42,7 +42,7 @@ pub mod unix_only { // @has doc_cfg/wasi_only/index.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ // 'Available on WASI only.' -// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\AWebAssembly\Z' +// @matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AWebAssembly\Z' // @count - '//*[@class="stab portability"]' 2 #[doc(cfg(target_os = "wasi"))] pub mod wasi_only { @@ -74,7 +74,7 @@ pub mod wasi_only { // the portability header is different on the module view versus the full view // @has doc_cfg/index.html -// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\Aavx\Z' +// @matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\Aavx\Z' // @has doc_cfg/fn.uses_target_feature.html // @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ diff --git a/tests/rustdoc/duplicate-cfg.rs b/tests/rustdoc/duplicate-cfg.rs index 1ac2e5232496..12846c5c17ad 100644 --- a/tests/rustdoc/duplicate-cfg.rs +++ b/tests/rustdoc/duplicate-cfg.rs @@ -2,8 +2,8 @@ #![feature(doc_cfg)] // @has 'foo/index.html' -// @matches '-' '//*[@class="item-left"]//*[@class="stab portability"]' '^sync$' -// @has '-' '//*[@class="item-left"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only' +// @matches '-' '//*[@class="item-name"]//*[@class="stab portability"]' '^sync$' +// @has '-' '//*[@class="item-name"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only' // @has 'foo/struct.Foo.html' // @has '-' '//*[@class="stab portability"]' 'sync' diff --git a/tests/rustdoc/glob-shadowing-const.rs b/tests/rustdoc/glob-shadowing-const.rs index 5b786cf53f29..58fe8173e038 100644 --- a/tests/rustdoc/glob-shadowing-const.rs +++ b/tests/rustdoc/glob-shadowing-const.rs @@ -15,6 +15,6 @@ mod sub4 { pub use sub4::inner::*; // @has 'foo/index.html' -// @has - '//div[@class="item-right docblock-short"]' '1' -// @!has - '//div[@class="item-right docblock-short"]' '0' +// @has - '//div[@class="desc docblock-short"]' '1' +// @!has - '//div[@class="desc docblock-short"]' '0' fn main() { assert_eq!(X, 1); } diff --git a/tests/rustdoc/glob-shadowing.rs b/tests/rustdoc/glob-shadowing.rs index 2668b3334979..c117b9d64894 100644 --- a/tests/rustdoc/glob-shadowing.rs +++ b/tests/rustdoc/glob-shadowing.rs @@ -1,17 +1,17 @@ // @has 'glob_shadowing/index.html' -// @count - '//div[@class="item-left"]' 6 -// @!has - '//div[@class="item-right docblock-short"]' 'sub1::describe' -// @has - '//div[@class="item-right docblock-short"]' 'sub2::describe' +// @count - '//div[@class="item-name"]' 6 +// @!has - '//div[@class="desc docblock-short"]' 'sub1::describe' +// @has - '//div[@class="desc docblock-short"]' 'sub2::describe' -// @!has - '//div[@class="item-right docblock-short"]' 'sub1::describe2' +// @!has - '//div[@class="desc docblock-short"]' 'sub1::describe2' -// @!has - '//div[@class="item-right docblock-short"]' 'sub1::prelude' -// @has - '//div[@class="item-right docblock-short"]' 'mod::prelude' +// @!has - '//div[@class="desc docblock-short"]' 'sub1::prelude' +// @has - '//div[@class="desc docblock-short"]' 'mod::prelude' -// @has - '//div[@class="item-right docblock-short"]' 'sub1::Foo (struct)' -// @has - '//div[@class="item-right docblock-short"]' 'mod::Foo (function)' +// @has - '//div[@class="desc docblock-short"]' 'sub1::Foo (struct)' +// @has - '//div[@class="desc docblock-short"]' 'mod::Foo (function)' -// @has - '//div[@class="item-right docblock-short"]' 'sub4::inner::X' +// @has - '//div[@class="desc docblock-short"]' 'sub4::inner::X' // @has 'glob_shadowing/fn.describe.html' // @has - '//div[@class="docblock"]' 'sub2::describe' diff --git a/tests/rustdoc/inline_cross/macros.rs b/tests/rustdoc/inline_cross/macros.rs index b11d5b6c4fa2..a41b9c5b1972 100644 --- a/tests/rustdoc/inline_cross/macros.rs +++ b/tests/rustdoc/inline_cross/macros.rs @@ -6,9 +6,9 @@ extern crate macros; -// @has foo/index.html '//*[@class="item-left"]/span[@class="stab deprecated"]' \ +// @has foo/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \ // Deprecated -// @has - '//*[@class="item-left"]/span[@class="stab unstable"]' \ +// @has - '//*[@class="item-name"]/span[@class="stab unstable"]' \ // Experimental // @has foo/macro.my_macro.html diff --git a/tests/rustdoc/internal.rs b/tests/rustdoc/internal.rs index caad43a087c4..27b0897689e0 100644 --- a/tests/rustdoc/internal.rs +++ b/tests/rustdoc/internal.rs @@ -3,12 +3,12 @@ // Check that the unstable marker is not added for "rustc_private". // @!matches internal/index.html \ -// '//*[@class="item-right docblock-short"]/span[@class="stab unstable"]' \ +// '//*[@class="desc docblock-short"]/span[@class="stab unstable"]' \ // '' // @!matches internal/index.html \ -// '//*[@class="item-right docblock-short"]/span[@class="stab internal"]' \ +// '//*[@class="desc docblock-short"]/span[@class="stab internal"]' \ // '' -// @matches - '//*[@class="item-right docblock-short"]' 'Docs' +// @matches - '//*[@class="desc docblock-short"]' 'Docs' // @!has internal/struct.S.html '//*[@class="stab unstable"]' '' // @!has internal/struct.S.html '//*[@class="stab internal"]' '' diff --git a/tests/rustdoc/issue-32374.rs b/tests/rustdoc/issue-32374.rs index 1153a745b0bf..985bf03a1215 100644 --- a/tests/rustdoc/issue-32374.rs +++ b/tests/rustdoc/issue-32374.rs @@ -2,11 +2,11 @@ #![doc(issue_tracker_base_url = "https://issue_url/")] #![unstable(feature = "test", issue = "32374")] -// @matches issue_32374/index.html '//*[@class="item-left"]/span[@class="stab deprecated"]' \ +// @matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \ // 'Deprecated' -// @matches issue_32374/index.html '//*[@class="item-left"]/span[@class="stab unstable"]' \ +// @matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab unstable"]' \ // 'Experimental' -// @matches issue_32374/index.html '//*[@class="item-right docblock-short"]/text()' 'Docs' +// @matches issue_32374/index.html '//*[@class="desc docblock-short"]/text()' 'Docs' // @has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' '👎' // @has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' \ diff --git a/tests/rustdoc/issue-46377.rs b/tests/rustdoc/issue-46377.rs index 4489f038c593..1311b4721e2f 100644 --- a/tests/rustdoc/issue-46377.rs +++ b/tests/rustdoc/issue-46377.rs @@ -1,3 +1,3 @@ -// @has 'issue_46377/index.html' '//*[@class="item-right docblock-short"]' 'Check out this struct!' +// @has 'issue_46377/index.html' '//*[@class="desc docblock-short"]' 'Check out this struct!' /// # Check out this struct! pub struct SomeStruct; diff --git a/tests/rustdoc/issue-55364.rs b/tests/rustdoc/issue-55364.rs index b987da30ed29..941cb3ce1ca5 100644 --- a/tests/rustdoc/issue-55364.rs +++ b/tests/rustdoc/issue-55364.rs @@ -29,8 +29,8 @@ pub mod subone { // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' // Though there should be such links later -// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left"]/a[@class="fn"][@href="fn.foo.html"]' 'foo' -// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left"]/a[@class="fn"][@href="fn.bar.html"]' 'bar' +// @has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.foo.html"]' 'foo' +// @has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.bar.html"]' 'bar' /// See either [foo] or [bar]. pub mod subtwo { @@ -68,8 +68,8 @@ pub mod subthree { // Next we go *deeper* - In order to ensure it's not just "this or parent" // we test `crate::` and a `super::super::...` chain // @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html -// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo' -// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar' +// @has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo' +// @has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar' pub mod subfour { pub mod subfive { pub mod subsix { diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/issue-95873.rs index 3df93eb7cf16..83f1f2f75bf0 100644 --- a/tests/rustdoc/issue-95873.rs +++ b/tests/rustdoc/issue-95873.rs @@ -1,2 +1,2 @@ -// @has issue_95873/index.html "//*[@class='item-left']" "pub use ::std as x;" +// @has issue_95873/index.html "//*[@class='item-name']" "pub use ::std as x;" pub use ::std as x; diff --git a/tests/rustdoc/reexport-check.rs b/tests/rustdoc/reexport-check.rs index 94fa03385322..5908d2150f2a 100644 --- a/tests/rustdoc/reexport-check.rs +++ b/tests/rustdoc/reexport-check.rs @@ -8,13 +8,13 @@ extern crate reexport_check; #[allow(deprecated, deprecated_in_future)] pub use std::i32; // @!has 'foo/index.html' '//code' 'pub use self::string::String;' -// @has 'foo/index.html' '//div[@class="item-left"]' 'String' +// @has 'foo/index.html' '//div[@class="item-name"]' 'String' pub use std::string::String; // i32 is deprecated, String is not // @count 'foo/index.html' '//span[@class="stab deprecated"]' 1 -// @has 'foo/index.html' '//div[@class="item-right docblock-short"]' 'Docs in original' +// @has 'foo/index.html' '//div[@class="desc docblock-short"]' 'Docs in original' // this is a no-op, but shows what happens if there's an attribute that isn't a doc-comment #[doc(inline)] pub use reexport_check::S; diff --git a/tests/rustdoc/short-docblock-codeblock.rs b/tests/rustdoc/short-docblock-codeblock.rs index 3c5fa7b36adb..7ecd80b8c723 100644 --- a/tests/rustdoc/short-docblock-codeblock.rs +++ b/tests/rustdoc/short-docblock-codeblock.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -// @count foo/index.html '//*[@class="item-right docblock-short"]' 0 +// @count foo/index.html '//*[@class="desc docblock-short"]' 0 /// ``` /// let x = 12; diff --git a/tests/rustdoc/short-docblock.rs b/tests/rustdoc/short-docblock.rs index 1a8a689be1d1..791d3547c9fe 100644 --- a/tests/rustdoc/short-docblock.rs +++ b/tests/rustdoc/short-docblock.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/index.html '//*[@class="item-right docblock-short"]' 'fooo' -// @!has foo/index.html '//*[@class="item-right docblock-short"]/h1' 'fooo' +// @has foo/index.html '//*[@class="desc docblock-short"]' 'fooo' +// @!has foo/index.html '//*[@class="desc docblock-short"]/h1' 'fooo' // @has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' 'fooo' /// # fooo @@ -9,8 +9,8 @@ /// foo pub fn foo() {} -// @has foo/index.html '//*[@class="item-right docblock-short"]' 'mooood' -// @!has foo/index.html '//*[@class="item-right docblock-short"]/h2' 'mooood' +// @has foo/index.html '//*[@class="desc docblock-short"]' 'mooood' +// @!has foo/index.html '//*[@class="desc docblock-short"]/h2' 'mooood' // @has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' 'mooood' /// ## mooood @@ -18,7 +18,7 @@ pub fn foo() {} /// foo mod pub mod foo {} -// @has foo/index.html '//*[@class="item-right docblock-short"]/a[@href=\ +// @has foo/index.html '//*[@class="desc docblock-short"]/a[@href=\ // "https://nougat.world"]/code' 'nougat' /// [`nougat`](https://nougat.world) From efbf6547cfeb579c0fbed4306086708d31f7a59d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 12 Feb 2023 14:55:38 +0400 Subject: [PATCH 07/67] resolve: Fix doc links referring to other crates when documenting proc macro crates directly --- compiler/rustc_resolve/src/late.rs | 7 ++++- tests/rustdoc-ui/intra-doc/proc-macro-doc.rs | 27 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-ui/intra-doc/proc-macro-doc.rs diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bd74a010fa3e..7dc9e6ad4b07 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4206,7 +4206,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if let Some(res) = res && let Some(def_id) = res.opt_def_id() && !def_id.is_local() - && self.r.session.crate_types().contains(&CrateType::ProcMacro) { + && self.r.session.crate_types().contains(&CrateType::ProcMacro) + && matches!(self.r.session.opts.resolve_doc_links, ResolveDocLinks::ExportedMetadata) { // Encoding foreign def ids in proc macro crate metadata will ICE. return None; } @@ -4276,6 +4277,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .filter_map(|tr| { if !tr.def_id.is_local() && self.r.session.crate_types().contains(&CrateType::ProcMacro) + && matches!( + self.r.session.opts.resolve_doc_links, + ResolveDocLinks::ExportedMetadata + ) { // Encoding foreign def ids in proc macro crate metadata will ICE. return None; diff --git a/tests/rustdoc-ui/intra-doc/proc-macro-doc.rs b/tests/rustdoc-ui/intra-doc/proc-macro-doc.rs new file mode 100644 index 000000000000..8335fc902cc5 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/proc-macro-doc.rs @@ -0,0 +1,27 @@ +// check-pass +// force-host +// no-prefer-dynamic +// compile-flags: --crate-type proc-macro + +#![deny(rustdoc::broken_intra_doc_links)] + +extern crate proc_macro; +use proc_macro::*; + +/// [`Unpin`] +#[proc_macro_derive(F)] +pub fn derive_(t: proc_macro::TokenStream) -> proc_macro::TokenStream { + t +} + +/// [`Vec`] +#[proc_macro_attribute] +pub fn attr(t: proc_macro::TokenStream, _: proc_macro::TokenStream) -> proc_macro::TokenStream { + t +} + +/// [`std::fs::File`] +#[proc_macro] +pub fn func(t: proc_macro::TokenStream) -> proc_macro::TokenStream { + t +} From 6da64379ab1ed85a186011445edf22703bb7eb8f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 16 Feb 2023 19:49:31 +0200 Subject: [PATCH 08/67] "Basic usage" is redundant for there is just one example --- library/core/src/result.rs | 42 -------------------------------------- 1 file changed, 42 deletions(-) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 7596e9cc005e..208b220c24a9 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -525,8 +525,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(-3); /// assert_eq!(x.is_ok(), true); @@ -572,8 +570,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(-3); /// assert_eq!(x.is_err(), false); @@ -627,8 +623,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(2); /// assert_eq!(x.ok(), Some(2)); @@ -658,8 +652,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(2); /// assert_eq!(x.err(), None); @@ -693,8 +685,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(2); /// assert_eq!(x.as_ref(), Ok(&2)); @@ -716,8 +706,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// fn mutate(r: &mut Result) { /// match r.as_mut() { @@ -812,8 +800,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let k = 21; /// @@ -841,8 +827,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// fn stringify(x: u32) -> String { format!("error code: {x}") } /// @@ -968,8 +952,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(7); /// assert_eq!(x.iter().next(), Some(&7)); @@ -989,8 +971,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let mut x: Result = Ok(7); /// match x.iter_mut().next() { @@ -1031,8 +1011,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ```should_panic /// let x: Result = Err("emergency failure"); /// x.expect("Testing expect"); // panics with `Testing expect: emergency failure` @@ -1160,8 +1138,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ```should_panic /// let x: Result = Ok(10); /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10` @@ -1222,8 +1198,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// # #![feature(never_type)] /// # #![feature(unwrap_infallible)] @@ -1259,8 +1233,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// # #![feature(never_type)] /// # #![feature(unwrap_infallible)] @@ -1298,8 +1270,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(2); /// let y: Result<&str, &str> = Err("late error"); @@ -1383,8 +1353,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(2); /// let y: Result = Err("late error"); @@ -1426,8 +1394,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// fn sq(x: u32) -> Result { Ok(x * x) } /// fn err(x: u32) -> Result { Err(x) } @@ -1456,8 +1422,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let default = 2; /// let x: Result = Ok(9); @@ -1487,8 +1451,6 @@ impl Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// fn count(x: &str) -> usize { x.len() } /// @@ -1752,8 +1714,6 @@ impl Result, E> { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// #![feature(result_flattening)] /// let x: Result, u32> = Ok(Ok("hello")); @@ -1842,8 +1802,6 @@ impl IntoIterator for Result { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let x: Result = Ok(5); /// let v: Vec = x.into_iter().collect(); From 5eba3f688cc6c2efc545c346675a5186da03e1fd Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Feb 2023 16:49:28 -0700 Subject: [PATCH 09/67] rustdoc: hide `reference` methods in search index --- src/librustdoc/formats/cache.rs | 4 ++++ tests/rustdoc-js-std/reference-shrink.js | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/rustdoc-js-std/reference-shrink.js diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index b1db16cfe3ca..d25824a88ee8 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -287,6 +287,10 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } else { let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); let did = match &*last { + ParentStackItem::Impl { + for_: clean::Type::BorrowedRef { type_, .. }, + .. + } => type_.def_id(&self.cache), ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache), ParentStackItem::Type(item_id) => item_id.as_def_id(), }; diff --git a/tests/rustdoc-js-std/reference-shrink.js b/tests/rustdoc-js-std/reference-shrink.js new file mode 100644 index 000000000000..f90be6d1bfd3 --- /dev/null +++ b/tests/rustdoc-js-std/reference-shrink.js @@ -0,0 +1,8 @@ +// exact-check + +const QUERY = 'reference::shrink'; + +const EXPECTED = { + // avoid including the method that's not going to be in the HTML + 'others': [], +}; From 0e3ae605bc245e0fb25e04c5f99d398d8a2b5c09 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 17 Feb 2023 10:35:19 -0700 Subject: [PATCH 10/67] Add comment explaining the search index tweak --- src/librustdoc/formats/cache.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index d25824a88ee8..fe7081b571a5 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -288,6 +288,12 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); let did = match &*last { ParentStackItem::Impl { + // impl Trait for &T { fn method(self); } + // + // When generating a function index with the above shape, we want it + // associated with `T`, not with the primitive reference type. It should + // show up as `T::method`, rather than `reference::method`, in the search + // results page. for_: clean::Type::BorrowedRef { type_, .. }, .. } => type_.def_id(&self.cache), From 6209e6c5983f93526da54db1b66beb3f1d658437 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 17 Feb 2023 14:48:25 -0600 Subject: [PATCH 11/67] Change src/etc/vscode_settings.json to always treat ./library as the sysroot source See https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/False.20error.20report.20for.20.60rust-analyzer.28private-field.29.60 for further discussion; previously this had various bugs. I tested go-to-definition on: - `use std::io::Write` in `src/bootstrap/setup.rs` - `use std::cell::RefCell` in `src/librustdoc/core.rs` - `use rustc_span::symbol::sym` in `src/librustdoc/core.rs` - `use std::fmt` in `compiler/rustc_span/src/symbol.rs` - `Global` in `library/alloc/src/alloc/tests.rs` The following things still don't work: - `Global.deallocate` in alloc/tests.rs. This function is under `cfg(not(test))`, so it can't be enabled without disabling RA in `tests.rs` altogether. I think this might be fixable by moving `library/alloc/src/alloc/tests.rs` to `library/alloc/tests/alloc/lib.rs`, so it's in a different crate, but I'd like to avoid blocking this improvement on that change. --- src/bootstrap/setup.rs | 1 + src/etc/vscode_settings.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 2b613ad50ee4..a027139df239 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -29,6 +29,7 @@ pub enum Profile { static SETTINGS_HASHES: &[&str] = &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", + "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", ]; static VSCODE_SETTINGS: &str = include_str!("../etc/vscode_settings.json"); diff --git a/src/etc/vscode_settings.json b/src/etc/vscode_settings.json index 04c34ce91c0b..dd01bfaa7252 100644 --- a/src/etc/vscode_settings.json +++ b/src/etc/vscode_settings.json @@ -1,7 +1,7 @@ { "rust-analyzer.check.invocationLocation": "root", "rust-analyzer.check.invocationStrategy": "once", - "rust-analyzer.checkOnSave.overrideCommand": [ + "rust-analyzer.check.overrideCommand": [ "python3", "x.py", "check", @@ -23,6 +23,6 @@ "check", "--json-output" ], - "rust-analyzer.cargo.sysroot": "./build/host/stage0-sysroot", + "rust-analyzer.cargo.sysrootSrc": "./library", "rust-analyzer.rustc.source": "./Cargo.toml" } From 34ba77d26064f9038fddd5349ceede088f8557f9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 18 Feb 2023 13:47:04 +0400 Subject: [PATCH 12/67] rustdoc: Do not use Footnotes and HeadingLinks when extracting doc links they do not add any `Link` events --- src/librustdoc/html/markdown.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index e4adee6ae4df..394bea3caee7 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1275,15 +1275,10 @@ pub(crate) fn markdown_links( .map(|link| links.borrow_mut().push(link)); None }; - let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut push)) - .into_offset_iter(); - // There's no need to thread an IdMap through to here because - // the IDs generated aren't going to be emitted anywhere. - let mut ids = IdMap::new(); - let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1)); - - for ev in iter { + for ev in Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut push)) + .into_offset_iter() + { if let Event::Start(Tag::Link( // `<>` links cannot be intra-doc links so we skip them. kind @ (LinkType::Inline From ccdb598d1b495952bdb09122dc53c39b40f23758 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 18 Feb 2023 14:10:38 +0400 Subject: [PATCH 13/67] rustdoc: Cleanup broken link callbacks --- compiler/rustc_resolve/src/rustdoc.rs | 23 ++++------ src/librustdoc/html/markdown.rs | 46 +++++++------------ .../intra-doc/unknown-disambiguator.stderr | 32 ++++++------- 3 files changed, 43 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 3425e24585cd..be49fbfaa295 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; -use std::cell::RefCell; use std::{cmp, mem}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -354,16 +353,14 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec| { - links.borrow_mut().push(preprocess_link(&link.reference)); - None - }; - for event in Parser::new_with_broken_link_callback(&doc, main_body_opts(), Some(&mut callback)) - { - if let Event::Start(Tag::Link(_, dest, _)) = event { - links.borrow_mut().push(preprocess_link(&dest)); - } - } - links.into_inner() + Parser::new_with_broken_link_callback( + &doc, + main_body_opts(), + Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + ) + .filter_map(|event| match event { + Event::Start(Tag::Link(_, dest, _)) => Some(preprocess_link(&dest)), + _ => None, + }) + .collect() } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 394bea3caee7..a34628991b24 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -34,7 +34,6 @@ use rustc_span::{Span, Symbol}; use once_cell::sync::Lazy; use std::borrow::Cow; -use std::cell::RefCell; use std::collections::VecDeque; use std::default::Default; use std::fmt::Write; @@ -1226,14 +1225,12 @@ pub(crate) struct MarkdownLink { pub(crate) fn markdown_links( md: &str, - filter_map: impl Fn(MarkdownLink) -> Option, + preprocess_link: impl Fn(MarkdownLink) -> Option, ) -> Vec { if md.is_empty() { return vec![]; } - let links = RefCell::new(vec![]); - // FIXME: remove this function once pulldown_cmark can provide spans for link definitions. let locate = |s: &str, fallback: Range| unsafe { let s_start = s.as_ptr(); @@ -1265,21 +1262,14 @@ pub(crate) fn markdown_links( } }; - let mut push = |link: BrokenLink<'_>| { - let span = span_for_link(&link.reference, link.span); - filter_map(MarkdownLink { - kind: LinkType::ShortcutUnknown, - link: link.reference.to_string(), - range: span, - }) - .map(|link| links.borrow_mut().push(link)); - None - }; - - for ev in Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut push)) - .into_offset_iter() - { - if let Event::Start(Tag::Link( + Parser::new_with_broken_link_callback( + md, + main_body_opts(), + Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + ) + .into_offset_iter() + .filter_map(|(event, span)| match event { + Event::Start(Tag::Link( // `<>` links cannot be intra-doc links so we skip them. kind @ (LinkType::Inline | LinkType::Reference @@ -1290,16 +1280,14 @@ pub(crate) fn markdown_links( | LinkType::ShortcutUnknown), dest, _, - )) = ev.0 - { - debug!("found link: {dest}"); - let span = span_for_link(&dest, ev.1); - filter_map(MarkdownLink { kind, link: dest.into_string(), range: span }) - .map(|link| links.borrow_mut().push(link)); - } - } - - links.into_inner() + )) => preprocess_link(MarkdownLink { + kind, + range: span_for_link(&dest, span), + link: dest.into_string(), + }), + _ => None, + }) + .collect() } #[derive(Debug)] diff --git a/tests/rustdoc-ui/intra-doc/unknown-disambiguator.stderr b/tests/rustdoc-ui/intra-doc/unknown-disambiguator.stderr index 19e541736bd0..741a7f51a77e 100644 --- a/tests/rustdoc-ui/intra-doc/unknown-disambiguator.stderr +++ b/tests/rustdoc-ui/intra-doc/unknown-disambiguator.stderr @@ -20,22 +20,6 @@ LL | //! Linking to [foo@banana] and [`bar@banana!()`]. | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators -error: unknown disambiguator `foo` - --> $DIR/unknown-disambiguator.rs:10:34 - | -LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. - | ^^^ - | - = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators - -error: unknown disambiguator `foo` - --> $DIR/unknown-disambiguator.rs:10:48 - | -LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. - | ^^^ - | - = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators - error: unknown disambiguator `` --> $DIR/unknown-disambiguator.rs:7:31 | @@ -52,5 +36,21 @@ LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()). | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +error: unknown disambiguator `foo` + --> $DIR/unknown-disambiguator.rs:10:34 + | +LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. + | ^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: unknown disambiguator `foo` + --> $DIR/unknown-disambiguator.rs:10:48 + | +LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. + | ^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + error: aborting due to 6 previous errors From 97e73eea84e8302ae08f88645538ee27485770d6 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 18 Feb 2023 14:56:31 +0400 Subject: [PATCH 14/67] doc links: Filter away autolinks in both rustc and rustdoc --- compiler/rustc_resolve/src/rustdoc.rs | 21 +++++++++++++++++++-- src/librustdoc/html/markdown.rs | 24 ++++++++---------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index be49fbfaa295..0c4b4bc3f626 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -1,4 +1,4 @@ -use pulldown_cmark::{BrokenLink, Event, Options, Parser, Tag}; +use pulldown_cmark::{BrokenLink, Event, LinkType, Options, Parser, Tag}; use rustc_ast as ast; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxHashMap; @@ -347,6 +347,21 @@ fn preprocess_link(link: &str) -> String { strip_generics_from_path(link).unwrap_or_else(|_| link.to_string()) } +/// Keep inline and reference links `[]`, +/// but skip autolinks `<>` which we never consider to be intra-doc links. +pub fn may_be_doc_link(link_type: LinkType) -> bool { + match link_type { + LinkType::Inline + | LinkType::Reference + | LinkType::ReferenceUnknown + | LinkType::Collapsed + | LinkType::CollapsedUnknown + | LinkType::Shortcut + | LinkType::ShortcutUnknown => true, + LinkType::Autolink | LinkType::Email => false, + } +} + /// Simplified version of `preprocessed_markdown_links` from rustdoc. /// Must return at least the same links as it, but may add some more links on top of that. pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec { @@ -359,7 +374,9 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec| Some((link.reference, "".into()))), ) .filter_map(|event| match event { - Event::Start(Tag::Link(_, dest, _)) => Some(preprocess_link(&dest)), + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + Some(preprocess_link(&dest)) + } _ => None, }) .collect() diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a34628991b24..9ef0b501c085 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -29,6 +29,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; pub(crate) use rustc_resolve::rustdoc::main_body_opts; +use rustc_resolve::rustdoc::may_be_doc_link; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; @@ -1269,22 +1270,13 @@ pub(crate) fn markdown_links( ) .into_offset_iter() .filter_map(|(event, span)| match event { - Event::Start(Tag::Link( - // `<>` links cannot be intra-doc links so we skip them. - kind @ (LinkType::Inline - | LinkType::Reference - | LinkType::ReferenceUnknown - | LinkType::Collapsed - | LinkType::CollapsedUnknown - | LinkType::Shortcut - | LinkType::ShortcutUnknown), - dest, - _, - )) => preprocess_link(MarkdownLink { - kind, - range: span_for_link(&dest, span), - link: dest.into_string(), - }), + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + preprocess_link(MarkdownLink { + kind: link_type, + range: span_for_link(&dest, span), + link: dest.into_string(), + }) + } _ => None, }) .collect() From f1e649b378576e268a3e31e981afdba4ede8cac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anselm=20Sch=C3=BCler?= Date: Mon, 16 Jan 2023 21:13:35 +0100 Subject: [PATCH 15/67] Update documentation of select_nth_unstable and select_nth_unstable_by and select_nth_unstable_by_key to state O(n log n) worst case complexity Also remove erronious / in doc comment --- library/core/src/slice/mod.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index c32caa144594..91bd2a54de22 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2708,8 +2708,10 @@ impl [T] { /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index`. Additionally, this reordering is /// unstable (i.e. any number of equal elements may end up at position `index`), in-place - /// (i.e. does not allocate), and *O*(*n*) worst-case. This function is also/ known as "kth - /// element" in other libraries. It returns a triplet of the following from the reordered slice: + /// (i.e. does not allocate), and *O*(*n*) on average. The worst-case performance is *O*(*n* log *n*). + /// This function is also known as "kth element" in other libraries. + /// + /// It returns a triplet of the following from the reordered slice: /// the subslice prior to `index`, the element at `index`, and the subslice after `index`; /// accordingly, the values in those two subslices will respectively all be less-than-or-equal-to /// and greater-than-or-equal-to the value of the element at `index`. @@ -2755,8 +2757,11 @@ impl [T] { /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index` using the comparator function. /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) worst-case. This function - /// is also known as "kth element" in other libraries. It returns a triplet of the following from + /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) on average. + /// The worst-case performance is *O*(*n* log *n*). This function is also known as + /// "kth element" in other libraries. + /// + /// It returns a triplet of the following from /// the slice reordered according to the provided comparator function: the subslice prior to /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in /// those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to @@ -2807,8 +2812,11 @@ impl [T] { /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index` using the key extraction function. /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) worst-case. This function - /// is also known as "kth element" in other libraries. It returns a triplet of the following from + /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) on average. + /// The worst-case performance is *O*(*n* log *n*). + /// This function is also known as "kth element" in other libraries. + /// + /// It returns a triplet of the following from /// the slice reordered according to the provided key extraction function: the subslice prior to /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in /// those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to From 0541a0c8c36a700e48ed5b7184686b515dbc5bf3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 7 Feb 2023 17:07:49 +0100 Subject: [PATCH 16/67] Allow reexports of items with same name but different types to both appear --- src/librustdoc/json/conversions.rs | 102 +++++++++++++++-------------- src/librustdoc/json/mod.rs | 15 ++--- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index bd95ec18650b..22db2390d080 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -38,7 +38,7 @@ impl JsonRenderer<'_> { Some(UrlFragment::UserWritten(_)) | None => *page_id, }; - (link.clone(), from_item_id(id.into(), self.tcx)) + (link.clone(), id_from_item_inner(id.into(), self.tcx, None)) }) .collect(); let docs = item.attrs.collapsed_doc_value(); @@ -50,7 +50,8 @@ impl JsonRenderer<'_> { .collect(); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); - let clean::Item { name, attrs: _, kind: _, item_id, cfg: _, .. } = item; + let clean::Item { name, item_id, .. } = item; + let id = id_from_item(&item, self.tcx); let inner = match *item.kind { clean::KeywordItem => return None, clean::StrippedItem(ref inner) => { @@ -69,7 +70,7 @@ impl JsonRenderer<'_> { _ => from_clean_item(item, self.tcx), }; Some(Item { - id: from_item_id_with_name(item_id, self.tcx, name), + id, crate_id: item_id.krate().as_u32(), name: name.map(|sym| sym.to_string()), span: span.and_then(|span| self.convert_span(span)), @@ -107,7 +108,7 @@ impl JsonRenderer<'_> { Some(ty::Visibility::Public) => Visibility::Public, Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate, Some(ty::Visibility::Restricted(did)) => Visibility::Restricted { - parent: from_item_id(did.into(), self.tcx), + parent: id_from_item_inner(did.into(), self.tcx, None), path: self.tcx.def_path(did).to_string_no_crate_verbose(), }, } @@ -207,54 +208,61 @@ impl FromWithTcx for TypeBindingKind { /// It generates an ID as follows: /// /// `CRATE_ID:ITEM_ID[:NAME_ID]` (if there is no name, NAME_ID is not generated). -pub(crate) fn from_item_id(item_id: ItemId, tcx: TyCtxt<'_>) -> Id { - from_item_id_with_name(item_id, tcx, None) -} +pub(crate) fn id_from_item_inner(item_id: ItemId, tcx: TyCtxt<'_>, extra: Option<&Id>) -> Id { + struct DisplayDefId<'a, 'b>(DefId, TyCtxt<'a>, Option<&'b Id>); -// FIXME: this function (and appending the name at the end of the ID) should be removed when -// reexports are not inlined anymore for json format. It should be done in #93518. -pub(crate) fn from_item_id_with_name(item_id: ItemId, tcx: TyCtxt<'_>, name: Option) -> Id { - struct DisplayDefId<'a>(DefId, TyCtxt<'a>, Option); - - impl<'a> fmt::Display for DisplayDefId<'a> { + impl<'a, 'b> fmt::Display for DisplayDefId<'a, 'b> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let DisplayDefId(def_id, tcx, name) = self; - let name = match name { - Some(name) => format!(":{}", name.as_u32()), - None => { - // We need this workaround because primitive types' DefId actually refers to - // their parent module, which isn't present in the output JSON items. So - // instead, we directly get the primitive symbol and convert it to u32 to - // generate the ID. - if matches!(tcx.def_kind(def_id), DefKind::Mod) && - let Some(prim) = tcx.get_attrs(*def_id, sym::doc) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) - .filter(|attr| attr.has_name(sym::primitive)) - .find_map(|attr| attr.value_str()) { - format!(":{}", prim.as_u32()) - } else { - tcx - .opt_item_name(*def_id) - .map(|n| format!(":{}", n.as_u32())) - .unwrap_or_default() - } - } + let DisplayDefId(def_id, tcx, extra) = self; + // We need this workaround because primitive types' DefId actually refers to + // their parent module, which isn't present in the output JSON items. So + // instead, we directly get the primitive symbol and convert it to u32 to + // generate the ID. + let s; + let extra = if let Some(e) = extra { + s = format!("-{}", e.0); + &s + } else { + "" }; - write!(f, "{}:{}{}", self.0.krate.as_u32(), u32::from(self.0.index), name) + let name = if matches!(tcx.def_kind(def_id), DefKind::Mod) && + let Some(prim) = tcx.get_attrs(*def_id, sym::doc) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::primitive)) + .find_map(|attr| attr.value_str()) { + format!(":{}", prim.as_u32()) + } else { + tcx + .opt_item_name(*def_id) + .map(|n| format!(":{}", n.as_u32())) + .unwrap_or_default() + }; + write!(f, "{}:{}{name}{extra}", self.0.krate.as_u32(), u32::from(self.0.index)) } } match item_id { - ItemId::DefId(did) => Id(format!("{}", DisplayDefId(did, tcx, name))), + ItemId::DefId(did) => Id(format!("{}", DisplayDefId(did, tcx, extra))), ItemId::Blanket { for_, impl_id } => { - Id(format!("b:{}-{}", DisplayDefId(impl_id, tcx, None), DisplayDefId(for_, tcx, name))) + Id(format!("b:{}-{}", DisplayDefId(impl_id, tcx, None), DisplayDefId(for_, tcx, extra))) } ItemId::Auto { for_, trait_ } => { - Id(format!("a:{}-{}", DisplayDefId(trait_, tcx, None), DisplayDefId(for_, tcx, name))) + Id(format!("a:{}-{}", DisplayDefId(trait_, tcx, None), DisplayDefId(for_, tcx, extra))) } } } +pub(crate) fn id_from_item(item: &clean::Item, tcx: TyCtxt<'_>) -> Id { + match *item.kind { + clean::ItemKind::ImportItem(ref import) => { + let extra = + import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None)); + id_from_item_inner(item.item_id, tcx, extra.as_ref()) + } + _ => id_from_item_inner(item.item_id, tcx, None), + } +} + fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { use clean::ItemKind::*; let name = item.name; @@ -525,7 +533,7 @@ impl FromWithTcx for Path { fn from_tcx(path: clean::Path, tcx: TyCtxt<'_>) -> Path { Path { name: path.whole_name(), - id: from_item_id(path.def_id().into(), tcx), + id: id_from_item_inner(path.def_id().into(), tcx, None), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), } } @@ -702,7 +710,7 @@ impl FromWithTcx for Import { Import { source: import.source.path.whole_name(), name, - id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)), + id: import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None)), glob, } } @@ -791,7 +799,7 @@ fn ids(items: impl IntoIterator, tcx: TyCtxt<'_>) -> Vec items .into_iter() .filter(|x| !x.is_stripped() && !x.is_keyword()) - .map(|i| from_item_id_with_name(i.item_id, tcx, i.name)) + .map(|i| id_from_item(&i, tcx)) .collect() } @@ -801,12 +809,10 @@ fn ids_keeping_stripped( ) -> Vec> { items .into_iter() - .map(|i| { - if !i.is_stripped() && !i.is_keyword() { - Some(from_item_id_with_name(i.item_id, tcx, i.name)) - } else { - None - } - }) + .map( + |i| { + if !i.is_stripped() && !i.is_keyword() { Some(id_from_item(&i, tcx)) } else { None } + }, + ) .collect() } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index e3788fe57d01..10900efd488e 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -28,7 +28,7 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::FormatRenderer; -use crate::json::conversions::{from_item_id, from_item_id_with_name, IntoWithTcx}; +use crate::json::conversions::{id_from_item, id_from_item_inner, IntoWithTcx}; use crate::{clean, try_err}; #[derive(Clone)] @@ -58,7 +58,7 @@ impl<'tcx> JsonRenderer<'tcx> { .map(|i| { let item = &i.impl_item; self.item(item.clone()).unwrap(); - from_item_id_with_name(item.item_id, self.tcx, item.name) + id_from_item(&item, self.tcx) }) .collect() }) @@ -89,7 +89,7 @@ impl<'tcx> JsonRenderer<'tcx> { if item.item_id.is_local() || is_primitive_impl { self.item(item.clone()).unwrap(); - Some(from_item_id_with_name(item.item_id, self.tcx, item.name)) + Some(id_from_item(&item, self.tcx)) } else { None } @@ -150,7 +150,6 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { // Flatten items that recursively store other items item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap()); - let name = item.name; let item_id = item.item_id; if let Some(mut new_item) = self.convert_item(item) { let can_be_ignored = match new_item.inner { @@ -193,10 +192,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { | types::ItemEnum::Macro(_) | types::ItemEnum::ProcMacro(_) => false, }; - let removed = self - .index - .borrow_mut() - .insert(from_item_id_with_name(item_id, self.tcx, name), new_item.clone()); + let removed = self.index.borrow_mut().insert(new_item.id.clone(), new_item.clone()); // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check // to make sure the items are unique. The main place this happens is when an item, is @@ -207,6 +203,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { if !can_be_ignored { assert_eq!(old_item, new_item); } + trace!("replaced {:?}\nwith {:?}", old_item, new_item); } } @@ -246,7 +243,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { .chain(&self.cache.external_paths) .map(|(&k, &(ref path, kind))| { ( - from_item_id(k.into(), self.tcx), + id_from_item_inner(k.into(), self.tcx, None), types::ItemSummary { crate_id: k.krate.as_u32(), path: path.iter().map(|s| s.to_string()).collect(), From 7d47d7cbaa9e8d163818fbd006ffa5382e3e66c4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 7 Feb 2023 17:08:59 +0100 Subject: [PATCH 17/67] Add regression test for #107677 --- .../reexport/same_name_different_types.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/rustdoc-json/reexport/same_name_different_types.rs diff --git a/tests/rustdoc-json/reexport/same_name_different_types.rs b/tests/rustdoc-json/reexport/same_name_different_types.rs new file mode 100644 index 000000000000..2314a4eb9094 --- /dev/null +++ b/tests/rustdoc-json/reexport/same_name_different_types.rs @@ -0,0 +1,25 @@ +// Regression test for . + +#![feature(no_core)] +#![no_core] + +pub mod nested { + // @set foo_struct = "$.index[*][?(@.docs == 'Foo the struct')].id" + + /// Foo the struct + pub struct Foo {} + + // @set foo_fn = "$.index[*][?(@.docs == 'Foo the function')].id" + + #[allow(non_snake_case)] + /// Foo the function + pub fn Foo() {} +} + +// @ismany "$.index[*][?(@.inner.name == 'Foo' && @.kind == 'import')].inner.id" $foo_fn $foo_struct +// @ismany "$.index[*][?(@.inner.name == 'Bar' && @.kind == 'import')].inner.id" $foo_fn $foo_struct + +// @count "$.index[*][?(@.inner.name == 'Foo' && @.kind == 'import')]" 2 +pub use nested::Foo; +// @count "$.index[*][?(@.inner.name == 'Bar' && @.kind == 'import')]" 2 +pub use Foo as Bar; From 3adc0812542e6c9b13c78b85407dcc9d54885e0e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 8 Feb 2023 18:16:41 +0100 Subject: [PATCH 18/67] Fix bad handling of primitive types --- src/librustdoc/json/conversions.rs | 76 ++++++++++++++++++------------ src/librustdoc/json/mod.rs | 2 +- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 22db2390d080..89e61404ed0a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -38,7 +38,7 @@ impl JsonRenderer<'_> { Some(UrlFragment::UserWritten(_)) | None => *page_id, }; - (link.clone(), id_from_item_inner(id.into(), self.tcx, None)) + (link.clone(), id_from_item_inner(id.into(), self.tcx, None, None)) }) .collect(); let docs = item.attrs.collapsed_doc_value(); @@ -108,7 +108,7 @@ impl JsonRenderer<'_> { Some(ty::Visibility::Public) => Visibility::Public, Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate, Some(ty::Visibility::Restricted(did)) => Visibility::Restricted { - parent: id_from_item_inner(did.into(), self.tcx, None), + parent: id_from_item_inner(did.into(), self.tcx, None, None), path: self.tcx.def_path(did).to_string_no_crate_verbose(), }, } @@ -208,12 +208,17 @@ impl FromWithTcx for TypeBindingKind { /// It generates an ID as follows: /// /// `CRATE_ID:ITEM_ID[:NAME_ID]` (if there is no name, NAME_ID is not generated). -pub(crate) fn id_from_item_inner(item_id: ItemId, tcx: TyCtxt<'_>, extra: Option<&Id>) -> Id { - struct DisplayDefId<'a, 'b>(DefId, TyCtxt<'a>, Option<&'b Id>); +pub(crate) fn id_from_item_inner( + item_id: ItemId, + tcx: TyCtxt<'_>, + extra: Option<&Id>, + name: Option, +) -> Id { + struct DisplayDefId<'a, 'b>(DefId, TyCtxt<'a>, Option<&'b Id>, Option); impl<'a, 'b> fmt::Display for DisplayDefId<'a, 'b> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let DisplayDefId(def_id, tcx, extra) = self; + let DisplayDefId(def_id, tcx, extra, name) = self; // We need this workaround because primitive types' DefId actually refers to // their parent module, which isn't present in the output JSON items. So // instead, we directly get the primitive symbol and convert it to u32 to @@ -225,30 +230,43 @@ pub(crate) fn id_from_item_inner(item_id: ItemId, tcx: TyCtxt<'_>, extra: Option } else { "" }; - let name = if matches!(tcx.def_kind(def_id), DefKind::Mod) && - let Some(prim) = tcx.get_attrs(*def_id, sym::doc) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) - .filter(|attr| attr.has_name(sym::primitive)) - .find_map(|attr| attr.value_str()) { - format!(":{}", prim.as_u32()) - } else { - tcx - .opt_item_name(*def_id) - .map(|n| format!(":{}", n.as_u32())) - .unwrap_or_default() + let name = match name { + Some(name) => format!(":{}", name.as_u32()), + None => { + // We need this workaround because primitive types' DefId actually refers to + // their parent module, which isn't present in the output JSON items. So + // instead, we directly get the primitive symbol and convert it to u32 to + // generate the ID. + if matches!(tcx.def_kind(def_id), DefKind::Mod) && + let Some(prim) = tcx.get_attrs(*def_id, sym::doc) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::primitive)) + .find_map(|attr| attr.value_str()) { + format!(":{}", prim.as_u32()) + } else { + tcx + .opt_item_name(*def_id) + .map(|n| format!(":{}", n.as_u32())) + .unwrap_or_default() + } + } }; - write!(f, "{}:{}{name}{extra}", self.0.krate.as_u32(), u32::from(self.0.index)) + write!(f, "{}:{}{name}{extra}", def_id.krate.as_u32(), u32::from(def_id.index)) } } match item_id { - ItemId::DefId(did) => Id(format!("{}", DisplayDefId(did, tcx, extra))), - ItemId::Blanket { for_, impl_id } => { - Id(format!("b:{}-{}", DisplayDefId(impl_id, tcx, None), DisplayDefId(for_, tcx, extra))) - } - ItemId::Auto { for_, trait_ } => { - Id(format!("a:{}-{}", DisplayDefId(trait_, tcx, None), DisplayDefId(for_, tcx, extra))) - } + ItemId::DefId(did) => Id(format!("{}", DisplayDefId(did, tcx, extra, name))), + ItemId::Blanket { for_, impl_id } => Id(format!( + "b:{}-{}", + DisplayDefId(impl_id, tcx, None, None), + DisplayDefId(for_, tcx, extra, name) + )), + ItemId::Auto { for_, trait_ } => Id(format!( + "a:{}-{}", + DisplayDefId(trait_, tcx, None, None), + DisplayDefId(for_, tcx, extra, name) + )), } } @@ -256,10 +274,10 @@ pub(crate) fn id_from_item(item: &clean::Item, tcx: TyCtxt<'_>) -> Id { match *item.kind { clean::ItemKind::ImportItem(ref import) => { let extra = - import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None)); - id_from_item_inner(item.item_id, tcx, extra.as_ref()) + import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None, None)); + id_from_item_inner(item.item_id, tcx, extra.as_ref(), item.name) } - _ => id_from_item_inner(item.item_id, tcx, None), + _ => id_from_item_inner(item.item_id, tcx, None, item.name), } } @@ -533,7 +551,7 @@ impl FromWithTcx for Path { fn from_tcx(path: clean::Path, tcx: TyCtxt<'_>) -> Path { Path { name: path.whole_name(), - id: id_from_item_inner(path.def_id().into(), tcx, None), + id: id_from_item_inner(path.def_id().into(), tcx, None, None), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), } } @@ -710,7 +728,7 @@ impl FromWithTcx for Import { Import { source: import.source.path.whole_name(), name, - id: import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None)), + id: import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None, None)), glob, } } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 10900efd488e..a0785f32cf20 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -243,7 +243,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { .chain(&self.cache.external_paths) .map(|(&k, &(ref path, kind))| { ( - id_from_item_inner(k.into(), self.tcx, None), + id_from_item_inner(k.into(), self.tcx, None, None), types::ItemSummary { crate_id: k.krate.as_u32(), path: path.iter().map(|s| s.to_string()).collect(), From 1ae875f0a30b7a71755fac43a8c6fe00dd205d33 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 14 Feb 2023 14:22:25 +0100 Subject: [PATCH 19/67] Improve code readability --- src/librustdoc/json/conversions.rs | 27 +++++++++++++++++++-------- src/librustdoc/json/mod.rs | 4 ++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 89e61404ed0a..d5e9010eb4ed 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -38,7 +38,7 @@ impl JsonRenderer<'_> { Some(UrlFragment::UserWritten(_)) | None => *page_id, }; - (link.clone(), id_from_item_inner(id.into(), self.tcx, None, None)) + (link.clone(), id_from_item_default(id.into(), self.tcx)) }) .collect(); let docs = item.attrs.collapsed_doc_value(); @@ -108,7 +108,7 @@ impl JsonRenderer<'_> { Some(ty::Visibility::Public) => Visibility::Public, Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate, Some(ty::Visibility::Restricted(did)) => Visibility::Restricted { - parent: id_from_item_inner(did.into(), self.tcx, None, None), + parent: id_from_item_default(did.into(), self.tcx), path: self.tcx.def_path(did).to_string_no_crate_verbose(), }, } @@ -205,14 +205,25 @@ impl FromWithTcx for TypeBindingKind { } } +#[inline] +pub(crate) fn id_from_item_default(item_id: ItemId, tcx: TyCtxt<'_>) -> Id { + id_from_item_inner(item_id, tcx, None, None) +} + /// It generates an ID as follows: /// -/// `CRATE_ID:ITEM_ID[:NAME_ID]` (if there is no name, NAME_ID is not generated). +/// `CRATE_ID:ITEM_ID[:NAME_ID][-EXTRA]`: +/// * If there is no `name`, `NAME_ID` is not generated. +/// * If there is no `extra`, `EXTRA` is not generated. +/// +/// * `name` is the item's name if available (it's not for impl blocks for example). +/// * `extra` is used for reexports: it contains the ID of the reexported item. It is used to allow +/// to have items with the same name but different types to both appear in the generated JSON. pub(crate) fn id_from_item_inner( item_id: ItemId, tcx: TyCtxt<'_>, - extra: Option<&Id>, name: Option, + extra: Option<&Id>, ) -> Id { struct DisplayDefId<'a, 'b>(DefId, TyCtxt<'a>, Option<&'b Id>, Option); @@ -275,9 +286,9 @@ pub(crate) fn id_from_item(item: &clean::Item, tcx: TyCtxt<'_>) -> Id { clean::ItemKind::ImportItem(ref import) => { let extra = import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None, None)); - id_from_item_inner(item.item_id, tcx, extra.as_ref(), item.name) + id_from_item_inner(item.item_id, tcx, item.name, extra.as_ref()) } - _ => id_from_item_inner(item.item_id, tcx, None, item.name), + _ => id_from_item_inner(item.item_id, tcx, item.name, None), } } @@ -551,7 +562,7 @@ impl FromWithTcx for Path { fn from_tcx(path: clean::Path, tcx: TyCtxt<'_>) -> Path { Path { name: path.whole_name(), - id: id_from_item_inner(path.def_id().into(), tcx, None, None), + id: id_from_item_default(path.def_id().into(), tcx), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), } } @@ -728,7 +739,7 @@ impl FromWithTcx for Import { Import { source: import.source.path.whole_name(), name, - id: import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None, None)), + id: import.source.did.map(ItemId::from).map(|i| id_from_item_default(i, tcx)), glob, } } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index a0785f32cf20..08bceb59cfde 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -28,7 +28,7 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::FormatRenderer; -use crate::json::conversions::{id_from_item, id_from_item_inner, IntoWithTcx}; +use crate::json::conversions::{id_from_item, id_from_item_default, IntoWithTcx}; use crate::{clean, try_err}; #[derive(Clone)] @@ -243,7 +243,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { .chain(&self.cache.external_paths) .map(|(&k, &(ref path, kind))| { ( - id_from_item_inner(k.into(), self.tcx, None, None), + id_from_item_default(k.into(), self.tcx), types::ItemSummary { crate_id: k.krate.as_u32(), path: path.iter().map(|s| s.to_string()).collect(), From 88e39ee3149b813af3966388a0d49a92f947e322 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Feb 2023 15:54:35 +0100 Subject: [PATCH 20/67] make first component of dyn* use pointer layout+type, and adjust DynStar comment --- compiler/rustc_codegen_ssa/src/base.rs | 9 +-------- compiler/rustc_middle/src/ty/layout.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 2 +- compiler/rustc_type_ir/src/sty.rs | 8 +++----- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 023d38e93128..229e3d9dc5f8 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -39,7 +39,7 @@ use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Symbol; use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType}; -use rustc_target::abi::{Align, Size, VariantIdx}; +use rustc_target::abi::{Align, VariantIdx}; use std::collections::BTreeSet; use std::time::{Duration, Instant}; @@ -273,13 +273,6 @@ pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)), "destination type must be a dyn*" ); - // FIXME(dyn-star): this is probably not the best way to check if this is - // a pointer, and really we should ensure that the value is a suitable - // pointer earlier in the compilation process. - let src = match src_ty_and_layout.pointee_info_at(bx.cx(), Size::ZERO) { - Some(_) => bx.ptrtoint(src, bx.cx().type_isize()), - None => bx.bitcast(src, bx.type_isize()), - }; (src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info)) } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 3d0f9a5053cb..993191ee96a4 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -770,7 +770,7 @@ where ty::Dynamic(_, _, ty::DynStar) => { if i == 0 { - TyMaybeWithLayout::Ty(tcx.types.usize) + TyMaybeWithLayout::Ty(tcx.mk_mut_ptr(tcx.types.unit)) } else if i == 1 { // FIXME(dyn-star) same FIXME as above applies here too TyMaybeWithLayout::Ty( diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index b860fb6c9186..1a62794b0b44 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -193,7 +193,7 @@ fn layout_of_uncached<'tcx>( } ty::Dynamic(_, _, ty::DynStar) => { - let mut data = scalar_unit(Int(dl.ptr_sized_integer(), false)); + let mut data = scalar_unit(Pointer(AddressSpace::DATA)); data.valid_range_mut().start = 0; let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); vtable.valid_range_mut().start = 1; diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index e080726d91d5..ebe2b76aef33 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -26,11 +26,9 @@ pub enum DynKind { Dyn, /// A sized `dyn* Trait` object /// - /// These objects are represented as a `(data, vtable)` pair where `data` is a ptr-sized value - /// (often a pointer to the real object, but not necessarily) and `vtable` is a pointer to - /// the vtable for `dyn* Trait`. The representation is essentially the same as `&dyn Trait` - /// or similar, but the drop function included in the vtable is responsible for freeing the - /// underlying storage if needed. This allows a `dyn*` object to be treated agnostically with + /// These objects are represented as a `(data, vtable)` pair where `data` is a value of some + /// ptr-sized and ptr-aligned dynamically determined type `T` and `vtable` is a pointer to the + /// vtable of `impl T for Trait`. This allows a `dyn*` object to be treated agnostically with /// respect to whether it points to a `Box`, `Rc`, etc. DynStar, } From be55ad53a1da73203e73ad3ff8ef0bad6c955e6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 22:12:23 +0000 Subject: [PATCH 21/67] Remove default trait RPITIT candidates --- .../src/traits/project.rs | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 9b3249e58e8d..1c66fb257ebb 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -90,15 +90,7 @@ enum ProjectionCandidate<'tcx> { /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), - ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>), -} - -#[derive(PartialEq, Eq, Debug)] -enum ImplTraitInTraitCandidate<'tcx> { - // The `impl Trait` from a trait function's default body - Trait, - // A concrete type provided from a trait's `impl Trait` from an impl - Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), + ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), } enum ProjectionCandidateSet<'tcx> { @@ -1292,17 +1284,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( let tcx = selcx.tcx(); if tcx.def_kind(obligation.predicate.def_id) == DefKind::ImplTraitPlaceholder { let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id); - // If we are trying to project an RPITIT with trait's default `Self` parameter, - // then we must be within a default trait body. - if obligation.predicate.self_ty() - == ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.def_id).type_at(0) - && tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value() - { - candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( - ImplTraitInTraitCandidate::Trait, - )); - return; - } let trait_def_id = tcx.parent(trait_fn_def_id); let trait_substs = @@ -1313,9 +1294,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( let _ = selcx.infcx.commit_if_ok(|_| { match selcx.select(&obligation.with(tcx, trait_predicate)) { Ok(Some(super::ImplSource::UserDefined(data))) => { - candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( - ImplTraitInTraitCandidate::Impl(data), - )); + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data)); Ok(()) } Ok(None) => { @@ -1777,18 +1756,9 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } - ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => { + ProjectionCandidate::ImplTraitInTrait(data) => { confirm_impl_trait_in_trait_candidate(selcx, obligation, data) } - // If we're projecting an RPITIT for a default trait body, that's just - // the same def-id, but as an opaque type (with regular RPIT semantics). - ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress { - term: selcx - .tcx() - .mk_opaque(obligation.predicate.def_id, obligation.predicate.substs) - .into(), - obligations: vec![], - }, }; // When checking for cycle during evaluation, we compare predicates with From 82b52056fe99705b4234c5781eada2bd7ae6042e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Feb 2023 02:10:42 +0000 Subject: [PATCH 22/67] Check that built-in callable types validate their output type is `Sized` (in new solver) --- .../src/solve/assembly.rs | 12 +++++++++ .../src/solve/project_goals.rs | 22 ++++++++++------ .../src/solve/trait_goals.rs | 25 +++++++++++++------ .../builtin-fn-must-return-sized.rs | 17 +++++++++++++ .../builtin-fn-must-return-sized.stderr | 18 +++++++++++++ 5 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs create mode 100644 tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 126ec60b3d68..a9e0c5509a5e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -96,10 +96,22 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { impl_def_id: DefId, ) -> QueryResult<'tcx>; + // Consider a predicate we know holds (`assumption`) against a goal we're trying to prove. fn consider_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, + ) -> QueryResult<'tcx> { + Self::consider_assumption_with_certainty(ecx, goal, assumption, Certainty::Yes) + } + + // Consider a predicate we know holds (`assumption`) against a goal, unifying with + // the `assumption_certainty` if it satisfies the goal. + fn consider_assumption_with_certainty( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + assumption_certainty: Certainty, ) -> QueryResult<'tcx>; // A type implements an `auto trait` if its components do as well. These components diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 883342148f40..adf8a1690c83 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -260,10 +260,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }) } - fn consider_assumption( + fn consider_assumption_with_certainty( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, + assumption_certainty: Certainty, ) -> QueryResult<'tcx> { if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() && poly_projection_pred.projection_def_id() == goal.predicate.def_id() @@ -280,7 +281,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx.eq_term_and_make_canonical_response( goal, - subst_certainty, + subst_certainty.unify_and(assumption_certainty), assumption_projection_pred.term, ) }) @@ -329,22 +330,29 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); if let Some(tupled_inputs_and_output) = structural_traits::extract_tupled_inputs_and_output_from_callable( - ecx.tcx(), + tcx, goal.predicate.self_ty(), goal_kind, )? { + // A built-in `Fn` trait needs to check that its output is `Sized` + // (FIXME: technically we only need to check this if the type is a fn ptr...) + let output_is_sized_pred = tupled_inputs_and_output + .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); + let (_, output_is_sized_certainty) = + ecx.evaluate_goal(goal.with(tcx, output_is_sized_pred))?; + let pred = tupled_inputs_and_output .map_bound(|(inputs, output)| ty::ProjectionPredicate { - projection_ty: ecx - .tcx() + projection_ty: tcx .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]), term: output.into(), }) - .to_predicate(ecx.tcx()); - Self::consider_assumption(ecx, goal, pred) + .to_predicate(tcx); + Self::consider_assumption_with_certainty(ecx, goal, pred, output_is_sized_certainty) } else { ecx.make_canonical_response(Certainty::AMBIGUOUS) } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index d12e5f797fb9..dd47a5ae1507 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -6,6 +6,7 @@ use super::assembly; use super::infcx_ext::InferCtxtExt; use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; @@ -61,10 +62,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_assumption( + fn consider_assumption_with_certainty( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, + assumption_certainty: Certainty, ) -> QueryResult<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() && poly_trait_pred.def_id() == goal.predicate.def_id() @@ -78,7 +80,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; - ecx.evaluate_all_and_make_canonical_response(nested_goals) + ecx.evaluate_all(nested_goals).and_then(|certainty| { + ecx.make_canonical_response(certainty.unify_and(assumption_certainty)) + }) }) } else { Err(NoSolution) @@ -173,20 +177,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); if let Some(tupled_inputs_and_output) = structural_traits::extract_tupled_inputs_and_output_from_callable( - ecx.tcx(), + tcx, goal.predicate.self_ty(), goal_kind, )? { + // A built-in `Fn` trait needs to check that its output is `Sized` + // (FIXME: technically we only need to check this if the type is a fn ptr...) + let output_is_sized_pred = tupled_inputs_and_output + .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); + let (_, output_is_sized_certainty) = + ecx.evaluate_goal(goal.with(tcx, output_is_sized_pred))?; + let pred = tupled_inputs_and_output .map_bound(|(inputs, _)| { - ecx.tcx() - .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) }) - .to_predicate(ecx.tcx()); - Self::consider_assumption(ecx, goal, pred) + .to_predicate(tcx); + Self::consider_assumption_with_certainty(ecx, goal, pred, output_is_sized_certainty) } else { ecx.make_canonical_response(Certainty::AMBIGUOUS) } diff --git a/tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs new file mode 100644 index 000000000000..ba473653ecfe --- /dev/null +++ b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs @@ -0,0 +1,17 @@ +// compile-flags: -Ztrait-solver=next + +#![feature(fn_traits)] +#![feature(unboxed_closures)] +#![feature(tuple_trait)] + +use std::ops::Fn; +use std::marker::Tuple; + +fn foo, T: Tuple>(f: Option, t: T) { + let y = (f.unwrap()).call(t); +} + +fn main() { + foo:: str, _>(None, ()); + //~^ expected a `Fn<_>` closure, found `fn() -> str` +} diff --git a/tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr new file mode 100644 index 000000000000..f7551739b132 --- /dev/null +++ b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr @@ -0,0 +1,18 @@ +error[E0277]: expected a `Fn<_>` closure, found `fn() -> str` + --> $DIR/builtin-fn-must-return-sized.rs:15:27 + | +LL | foo:: str, _>(None, ()); + | --------------------- ^^^^ expected an `Fn<_>` closure, found `fn() -> str` + | | + | required by a bound introduced by this call + | + = help: the trait `Fn<_>` is not implemented for `fn() -> str` +note: required by a bound in `foo` + --> $DIR/builtin-fn-must-return-sized.rs:10:11 + | +LL | fn foo, T: Tuple>(f: Option, t: T) { + | ^^^^^ required by this bound in `foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 6402c98621ab620a16adac072abc10779cf60573 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 16 Feb 2023 03:04:08 +0000 Subject: [PATCH 23/67] Add consider_implied_clause --- .../src/solve/assembly.rs | 34 ++---- .../src/solve/project_goals.rs | 115 +++++++++--------- .../src/solve/trait_goals.rs | 50 ++++---- 3 files changed, 97 insertions(+), 102 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index a9e0c5509a5e..841169ac78d7 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -90,30 +90,22 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + // Consider a clause, which consists of a "assumption" and some "requirements", + // to satisfy a goal. If the requirements hold, then attempt to satisfy our + // goal by equating it with the assumption. + fn consider_implied_clause( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + requirements: impl IntoIterator>>, + ) -> QueryResult<'tcx>; + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, ) -> QueryResult<'tcx>; - // Consider a predicate we know holds (`assumption`) against a goal we're trying to prove. - fn consider_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx> { - Self::consider_assumption_with_certainty(ecx, goal, assumption, Certainty::Yes) - } - - // Consider a predicate we know holds (`assumption`) against a goal, unifying with - // the `assumption_certainty` if it satisfies the goal. - fn consider_assumption_with_certainty( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - assumption_certainty: Certainty, - ) -> QueryResult<'tcx>; - // A type implements an `auto trait` if its components do as well. These components // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( @@ -367,7 +359,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { - match G::consider_assumption(self, goal, assumption) { + match G::consider_implied_clause(self, goal, assumption, []) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result }) } @@ -414,7 +406,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs) { - match G::consider_assumption(self, goal, assumption) { + match G::consider_implied_clause(self, goal, assumption, []) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::AliasBound, result }) } @@ -464,7 +456,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty))) { - match G::consider_assumption(self, goal, assumption.predicate) { + match G::consider_implied_clause(self, goal, assumption.predicate, []) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index adf8a1690c83..48153b465b7e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -168,6 +168,37 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { self.trait_def_id(tcx) } + fn consider_implied_clause( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + requirements: impl IntoIterator>>, + ) -> QueryResult<'tcx> { + if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() + && poly_projection_pred.projection_def_id() == goal.predicate.def_id() + { + ecx.infcx.probe(|_| { + let assumption_projection_pred = + ecx.infcx.instantiate_binder_with_infer(poly_projection_pred); + let mut nested_goals = ecx.infcx.eq( + goal.param_env, + goal.predicate.projection_ty, + assumption_projection_pred.projection_ty, + )?; + nested_goals.extend(requirements); + let subst_certainty = ecx.evaluate_all(nested_goals)?; + + ecx.eq_term_and_make_canonical_response( + goal, + subst_certainty, + assumption_projection_pred.term, + ) + }) + } else { + Err(NoSolution) + } + } + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, @@ -260,36 +291,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }) } - fn consider_assumption_with_certainty( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - assumption_certainty: Certainty, - ) -> QueryResult<'tcx> { - if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() - && poly_projection_pred.projection_def_id() == goal.predicate.def_id() - { - ecx.infcx.probe(|_| { - let assumption_projection_pred = - ecx.infcx.instantiate_binder_with_infer(poly_projection_pred); - let nested_goals = ecx.infcx.eq( - goal.param_env, - goal.predicate.projection_ty, - assumption_projection_pred.projection_ty, - )?; - let subst_certainty = ecx.evaluate_all(nested_goals)?; - - ecx.eq_term_and_make_canonical_response( - goal, - subst_certainty.unify_and(assumption_certainty), - assumption_projection_pred.term, - ) - }) - } else { - Err(NoSolution) - } - } - fn consider_auto_trait_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -331,31 +332,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); - if let Some(tupled_inputs_and_output) = - structural_traits::extract_tupled_inputs_and_output_from_callable( - tcx, - goal.predicate.self_ty(), - goal_kind, - )? - { - // A built-in `Fn` trait needs to check that its output is `Sized` - // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output - .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); - let (_, output_is_sized_certainty) = - ecx.evaluate_goal(goal.with(tcx, output_is_sized_pred))?; + let Some(tupled_inputs_and_output) = + structural_traits::extract_tupled_inputs_and_output_from_callable( + tcx, + goal.predicate.self_ty(), + goal_kind, + )? else { + return ecx.make_canonical_response(Certainty::AMBIGUOUS); + }; + let output_is_sized_pred = tupled_inputs_and_output + .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); - let pred = tupled_inputs_and_output - .map_bound(|(inputs, output)| ty::ProjectionPredicate { - projection_ty: tcx - .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]), - term: output.into(), - }) - .to_predicate(tcx); - Self::consider_assumption_with_certainty(ecx, goal, pred, output_is_sized_certainty) - } else { - ecx.make_canonical_response(Certainty::AMBIGUOUS) - } + let pred = tupled_inputs_and_output + .map_bound(|(inputs, output)| ty::ProjectionPredicate { + projection_ty: tcx + .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]), + term: output.into(), + }) + .to_predicate(tcx); + // A built-in `Fn` impl only holds if the output is sized. + // (FIXME: technically we only need to check this if the type is a fn ptr...) + Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) } fn consider_builtin_tuple_candidate( @@ -474,7 +471,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { let term = substs.as_generator().return_ty().into(); - Self::consider_assumption( + Self::consider_implied_clause( ecx, goal, ty::Binder::dummy(ty::ProjectionPredicate { @@ -482,6 +479,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { term, }) .to_predicate(tcx), + // Technically, we need to check that the future type is Sized, + // but that's already proven by the generator being WF. + [], ) } @@ -511,7 +511,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { bug!("unexpected associated item `<{self_ty} as Generator>::{name}`") }; - Self::consider_assumption( + Self::consider_implied_clause( ecx, goal, ty::Binder::dummy(ty::ProjectionPredicate { @@ -521,6 +521,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { term, }) .to_predicate(tcx), + // Technically, we need to check that the future type is Sized, + // but that's already proven by the generator being WF. + [], ) } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index dd47a5ae1507..f2f25ef850a9 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -62,11 +62,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_assumption_with_certainty( + fn consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - assumption_certainty: Certainty, + requirements: impl IntoIterator>>, ) -> QueryResult<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() && poly_trait_pred.def_id() == goal.predicate.def_id() @@ -75,14 +75,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.infcx.probe(|_| { let assumption_trait_pred = ecx.infcx.instantiate_binder_with_infer(poly_trait_pred); - let nested_goals = ecx.infcx.eq( + let mut nested_goals = ecx.infcx.eq( goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; - ecx.evaluate_all(nested_goals).and_then(|certainty| { - ecx.make_canonical_response(certainty.unify_and(assumption_certainty)) - }) + nested_goals.extend(requirements); + ecx.evaluate_all_and_make_canonical_response(nested_goals) }) } else { Err(NoSolution) @@ -178,29 +177,25 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); - if let Some(tupled_inputs_and_output) = + let Some(tupled_inputs_and_output) = structural_traits::extract_tupled_inputs_and_output_from_callable( tcx, goal.predicate.self_ty(), goal_kind, - )? - { - // A built-in `Fn` trait needs to check that its output is `Sized` - // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output - .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); - let (_, output_is_sized_certainty) = - ecx.evaluate_goal(goal.with(tcx, output_is_sized_pred))?; + )? else { + return ecx.make_canonical_response(Certainty::AMBIGUOUS); + }; + let output_is_sized_pred = tupled_inputs_and_output + .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); - let pred = tupled_inputs_and_output - .map_bound(|(inputs, _)| { - tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) - }) - .to_predicate(tcx); - Self::consider_assumption_with_certainty(ecx, goal, pred, output_is_sized_certainty) - } else { - ecx.make_canonical_response(Certainty::AMBIGUOUS) - } + let pred = tupled_inputs_and_output + .map_bound(|(inputs, _)| { + tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + }) + .to_predicate(tcx); + // A built-in `Fn` impl only holds if the output is sized. + // (FIXME: technically we only need to check this if the type is a fn ptr...) + Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) } fn consider_builtin_tuple_candidate( @@ -236,6 +231,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } // Async generator unconditionally implement `Future` + // Technically, we need to check that the future output type is Sized, + // but that's already proven by the generator being WF. ecx.make_canonical_response(Certainty::Yes) } @@ -255,13 +252,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } let generator = substs.as_generator(); - Self::consider_assumption( + Self::consider_implied_clause( ecx, goal, ty::Binder::dummy( tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]), ) .to_predicate(tcx), + // Technically, we need to check that the generator types are Sized, + // but that's already proven by the generator being WF. + [], ) } From df52e2037ab73828a29c46addf07df5522b305e5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 7 Feb 2023 19:17:04 +0000 Subject: [PATCH 24/67] Use inttoptr to support usize as dyn* value, use pointercast to make sure pointers are compatible --- compiler/rustc_codegen_ssa/src/base.rs | 8 ++++++++ tests/ui/dyn-star/llvm-old-style-ptrs.rs | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/ui/dyn-star/llvm-old-style-ptrs.rs diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 229e3d9dc5f8..4e13d4dbcb7a 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -273,6 +273,14 @@ pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)), "destination type must be a dyn*" ); + // FIXME(dyn-star): We can remove this when all supported LLVMs use opaque ptrs only. + let unit_ptr = bx.cx().type_ptr_to(bx.cx().type_struct(&[], false)); + let src = match bx.cx().type_kind(bx.cx().backend_type(src_ty_and_layout)) { + TypeKind::Pointer => bx.pointercast(src, unit_ptr), + TypeKind::Integer => bx.inttoptr(src, unit_ptr), + // FIXME(dyn-star): We probably have to do a bitcast first, then inttoptr. + kind => bug!("unexpected TypeKind for left-hand side of `dyn*` cast: {kind:?}"), + }; (src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info)) } diff --git a/tests/ui/dyn-star/llvm-old-style-ptrs.rs b/tests/ui/dyn-star/llvm-old-style-ptrs.rs new file mode 100644 index 000000000000..d35519632bec --- /dev/null +++ b/tests/ui/dyn-star/llvm-old-style-ptrs.rs @@ -0,0 +1,23 @@ +// run-pass +// compile-flags: -Copt-level=0 -Cllvm-args=-opaque-pointers=0 + +// (opaque-pointers flag is called force-opaque-pointers in LLVM 13...) +// min-llvm-version: 14.0 + +// This test can be removed once non-opaque pointers are gone from LLVM, maybe. + +#![feature(dyn_star, pointer_like_trait)] +#![allow(incomplete_features)] + +use std::fmt::Debug; +use std::marker::PointerLike; + +fn make_dyn_star<'a>(t: impl PointerLike + Debug + 'a) -> dyn* Debug + 'a { + t as _ +} + +fn main() { + println!("{:?}", make_dyn_star(Box::new(1i32))); + println!("{:?}", make_dyn_star(2usize)); + println!("{:?}", make_dyn_star((3usize,))); +} From 1f11d841b5748133c2d51da0001fc88bc6b51e78 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Feb 2023 23:54:05 +0000 Subject: [PATCH 25/67] Add codegen test --- tests/codegen/function-arguments.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs index 96dfde18683e..63f051190310 100644 --- a/tests/codegen/function-arguments.rs +++ b/tests/codegen/function-arguments.rs @@ -1,6 +1,7 @@ // compile-flags: -O -C no-prepopulate-passes #![crate_type = "lib"] +#![feature(dyn_star)] use std::mem::MaybeUninit; use std::num::NonZeroU64; @@ -279,3 +280,9 @@ pub fn enum_id_1(x: Option>) -> Option> { pub fn enum_id_2(x: Option) -> Option { x } + +// CHECK: { {{i8\*|ptr}}, {{i.*\*|ptr}} } @dyn_star({{i8\*|ptr}} noundef %x.0, {{i.*\*|ptr}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %x.1) +#[no_mangle] +pub fn dyn_star(x: dyn* Drop) -> dyn* Drop { + x +} From e82cc656c822af7f1905b757a27cac5823fbc301 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 15 Feb 2023 03:42:45 +0000 Subject: [PATCH 26/67] Make dyn* have the same scalar pair ABI as corresponding fat pointer --- compiler/rustc_codegen_llvm/src/type_of.rs | 7 ++++++- tests/codegen/function-arguments.rs | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index cc8ff947fc31..9cda24bab87d 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -329,7 +329,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { ) -> &'a Type { // HACK(eddyb) special-case fat pointers until LLVM removes // pointee types, to avoid bitcasting every `OperandRef::deref`. - match self.ty.kind() { + match *self.ty.kind() { ty::Ref(..) | ty::RawPtr(_) => { return self.field(cx, index).llvm_type(cx); } @@ -339,6 +339,11 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate); } + // `dyn* Trait` has the same ABI as `*mut dyn Trait` + ty::Dynamic(bounds, region, ty::DynStar) => { + let ptr_ty = cx.tcx.mk_mut_ptr(cx.tcx.mk_dynamic(bounds, region, ty::Dyn)); + return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate); + } _ => {} } diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs index 63f051190310..d6f019016a5a 100644 --- a/tests/codegen/function-arguments.rs +++ b/tests/codegen/function-arguments.rs @@ -281,7 +281,9 @@ pub fn enum_id_2(x: Option) -> Option { x } -// CHECK: { {{i8\*|ptr}}, {{i.*\*|ptr}} } @dyn_star({{i8\*|ptr}} noundef %x.0, {{i.*\*|ptr}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %x.1) +// CHECK: { {{\{\}\*|ptr}}, {{.+}} } @dyn_star({{\{\}\*|ptr}} noundef %x.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %x.1) +// Expect an ABI something like `{ {}*, [3 x i64]* }`, but that's hard to match on generically, +// so do like the `trait_box` test and just match on `{{.+}}` for the vtable. #[no_mangle] pub fn dyn_star(x: dyn* Drop) -> dyn* Drop { x From 7f798c2b216db0bb7ebeb9dd863fbdf7668094c5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 15 Feb 2023 05:11:27 +0000 Subject: [PATCH 27/67] Emit the right types for vtable pointers when dropping dyn* --- compiler/rustc_codegen_ssa/src/mir/block.rs | 158 ++++++++++---------- 1 file changed, 78 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9af408646ae0..daaa4de1a7c8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -452,86 +452,84 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args1 = [place.llval]; &args1[..] }; - let (drop_fn, fn_abi) = match ty.kind() { - // FIXME(eddyb) perhaps move some of this logic into - // `Instance::resolve_drop_in_place`? - ty::Dynamic(_, _, ty::Dyn) => { - // IN THIS ARM, WE HAVE: - // ty = *mut (dyn Trait) - // which is: exists ( *mut T, Vtable ) - // args[0] args[1] - // - // args = ( Data, Vtable ) - // | - // v - // /-------\ - // | ... | - // \-------/ - // - let virtual_drop = Instance { - def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), - substs: drop_fn.substs, - }; - debug!("ty = {:?}", ty); - debug!("drop_fn = {:?}", drop_fn); - debug!("args = {:?}", args); - let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); - let vtable = args[1]; - // Truncate vtable off of args list - args = &args[..1]; - ( - meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) - .get_fn(bx, vtable, ty, &fn_abi), - fn_abi, - ) - } - ty::Dynamic(_, _, ty::DynStar) => { - // IN THIS ARM, WE HAVE: - // ty = *mut (dyn* Trait) - // which is: *mut exists (T, Vtable) - // - // args = [ * ] - // | - // v - // ( Data, Vtable ) - // | - // v - // /-------\ - // | ... | - // \-------/ - // - // - // WE CAN CONVERT THIS INTO THE ABOVE LOGIC BY DOING - // - // data = &(*args[0]).0 // gives a pointer to Data above (really the same pointer) - // vtable = (*args[0]).1 // loads the vtable out - // (data, vtable) // an equivalent Rust `*mut dyn Trait` - // - // SO THEN WE CAN USE THE ABOVE CODE. - let virtual_drop = Instance { - def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), - substs: drop_fn.substs, - }; - debug!("ty = {:?}", ty); - debug!("drop_fn = {:?}", drop_fn); - debug!("args = {:?}", args); - let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); - let data = args[0]; - let data_ty = bx.cx().backend_type(place.layout); - let vtable_ptr = - bx.gep(data_ty, data, &[bx.cx().const_i32(0), bx.cx().const_i32(1)]); - let vtable = bx.load(bx.type_i8p(), vtable_ptr, abi::Align::ONE); - // Truncate vtable off of args list - args = &args[..1]; - debug!("args' = {:?}", args); - ( - meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) - .get_fn(bx, vtable, ty, &fn_abi), - fn_abi, - ) - } - _ => (bx.get_fn_addr(drop_fn), bx.fn_abi_of_instance(drop_fn, ty::List::empty())), - }; + let (drop_fn, fn_abi) = + match ty.kind() { + // FIXME(eddyb) perhaps move some of this logic into + // `Instance::resolve_drop_in_place`? + ty::Dynamic(_, _, ty::Dyn) => { + // IN THIS ARM, WE HAVE: + // ty = *mut (dyn Trait) + // which is: exists ( *mut T, Vtable ) + // args[0] args[1] + // + // args = ( Data, Vtable ) + // | + // v + // /-------\ + // | ... | + // \-------/ + // + let virtual_drop = Instance { + def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), + substs: drop_fn.substs, + }; + debug!("ty = {:?}", ty); + debug!("drop_fn = {:?}", drop_fn); + debug!("args = {:?}", args); + let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); + let vtable = args[1]; + // Truncate vtable off of args list + args = &args[..1]; + ( + meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) + .get_fn(bx, vtable, ty, &fn_abi), + fn_abi, + ) + } + ty::Dynamic(_, _, ty::DynStar) => { + // IN THIS ARM, WE HAVE: + // ty = *mut (dyn* Trait) + // which is: *mut exists (T, Vtable) + // + // args = [ * ] + // | + // v + // ( Data, Vtable ) + // | + // v + // /-------\ + // | ... | + // \-------/ + // + // + // WE CAN CONVERT THIS INTO THE ABOVE LOGIC BY DOING + // + // data = &(*args[0]).0 // gives a pointer to Data above (really the same pointer) + // vtable = (*args[0]).1 // loads the vtable out + // (data, vtable) // an equivalent Rust `*mut dyn Trait` + // + // SO THEN WE CAN USE THE ABOVE CODE. + let virtual_drop = Instance { + def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), + substs: drop_fn.substs, + }; + debug!("ty = {:?}", ty); + debug!("drop_fn = {:?}", drop_fn); + debug!("args = {:?}", args); + let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); + let meta_ptr = place.project_field(bx, 1); + let meta = bx.load_operand(meta_ptr); + // Truncate vtable off of args list + args = &args[..1]; + debug!("args' = {:?}", args); + ( + meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) + .get_fn(bx, meta.immediate(), ty, &fn_abi), + fn_abi, + ) + } + _ => (bx.get_fn_addr(drop_fn), bx.fn_abi_of_instance(drop_fn, ty::List::empty())), + }; helper.do_call( self, bx, From f4a4a3147955ceb359204d85a74e15ee9d98046b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 17 Feb 2023 21:01:22 +0000 Subject: [PATCH 28/67] Don't ICE on bound types in sized conditions --- .../src/traits/select/mod.rs | 9 +++--- .../non_lifetime_binders/bad-sized-cond.rs | 13 +++++++++ .../bad-sized-cond.stderr | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs create mode 100644 tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e6fc9bb92397..4b15dd408b37 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2148,12 +2148,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { })) } - ty::Alias(..) | ty::Param(_) => None, + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, ty::Infer(ty::TyVar(_)) => Ambiguous, - ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + // We can make this an ICE if/once we actually instantiate the trait obligation. + ty::Bound(..) => None, + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } } diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs new file mode 100644 index 000000000000..1612de7fc45c --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs @@ -0,0 +1,13 @@ +#![feature(non_lifetime_binders)] +//~^ WARN is incomplete and may not be safe + +pub fn foo() +where + for V: Sized, +{ +} + +fn main() { + foo(); + //~^ ERROR the size for values of type `V` cannot be known at compilation time +} diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr new file mode 100644 index 000000000000..eeb3baf010f2 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr @@ -0,0 +1,28 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bad-sized-cond.rs:1:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `V` cannot be known at compilation time + --> $DIR/bad-sized-cond.rs:11:5 + | +LL | foo(); + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `V` +note: required by a bound in `foo` + --> $DIR/bad-sized-cond.rs:6:15 + | +LL | pub fn foo() + | --- required by a bound in this +LL | where +LL | for V: Sized, + | ^^^^^ required by this bound in `foo` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. From ec40b1a3938ea0f7ae27b4bffe62bd41dc8015af Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 17 Feb 2023 21:15:18 +0000 Subject: [PATCH 29/67] Collapse placeholders to root universe in canonicalizer if not preserving universes --- .../src/infer/canonical/canonicalizer.rs | 13 +++++-- .../non_lifetime_binders/bad-sized-cond.rs | 8 ++++ .../bad-sized-cond.stderr | 38 ++++++++++++++++++- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 2b33d31994f2..b736a416e4a1 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -418,10 +418,15 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { bug!("encountered a fresh type during canonicalization") } - ty::Placeholder(placeholder) => self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) }, - t, - ), + ty::Placeholder(mut placeholder) => { + if !self.canonicalize_mode.preserve_universes() { + placeholder.universe = ty::UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) }, + t, + ) + } ty::Bound(debruijn, _) => { if debruijn >= self.binder_index { diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs index 1612de7fc45c..3128e0f31384 100644 --- a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs @@ -7,7 +7,15 @@ where { } +pub fn bar() +where + for V: IntoIterator, +{ +} + fn main() { foo(); //~^ ERROR the size for values of type `V` cannot be known at compilation time + + bar(); } diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr index eeb3baf010f2..3af115d897d1 100644 --- a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr @@ -8,7 +8,7 @@ LL | #![feature(non_lifetime_binders)] = note: `#[warn(incomplete_features)]` on by default error[E0277]: the size for values of type `V` cannot be known at compilation time - --> $DIR/bad-sized-cond.rs:11:5 + --> $DIR/bad-sized-cond.rs:17:5 | LL | foo(); | ^^^ doesn't have a size known at compile-time @@ -23,6 +23,40 @@ LL | where LL | for V: Sized, | ^^^^^ required by this bound in `foo` -error: aborting due to previous error; 1 warning emitted +error[E0277]: the size for values of type `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` cannot be known at compilation time + --> $DIR/bad-sized-cond.rs:20:5 + | +LL | bar(); + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` + = note: required for `V` to implement `IntoIterator` +note: required by a bound in `bar` + --> $DIR/bad-sized-cond.rs:12:15 + | +LL | pub fn bar() + | --- required by a bound in this +LL | where +LL | for V: IntoIterator, + | ^^^^^^^^^^^^ required by this bound in `bar` + +error[E0277]: `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` is not an iterator + --> $DIR/bad-sized-cond.rs:20:5 + | +LL | bar(); + | ^^^ `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` is not an iterator + | + = help: the trait `Iterator` is not implemented for `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` + = note: required for `V` to implement `IntoIterator` +note: required by a bound in `bar` + --> $DIR/bad-sized-cond.rs:12:15 + | +LL | pub fn bar() + | --- required by a bound in this +LL | where +LL | for V: IntoIterator, + | ^^^^^^^^^^^^ required by this bound in `bar` + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. From 6f3706ea71a89a431acf7f84573b87c2ac98b0c7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 17 Feb 2023 21:21:27 +0000 Subject: [PATCH 30/67] Pretty placeholders using their names --- compiler/rustc_middle/src/ty/print/pretty.rs | 5 ++++- tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs | 2 ++ .../traits/non_lifetime_binders/bad-sized-cond.stderr | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1e59983583b1..a101127104de 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -735,7 +735,10 @@ pub trait PrettyPrinter<'tcx>: p!(print(data)) } } - ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + ty::Placeholder(placeholder) => match placeholder.name { + ty::BoundTyKind::Anon(_) => p!(write("Placeholder({:?})", placeholder)), + ty::BoundTyKind::Param(_, name) => p!(write("{}", name)), + }, ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { // We use verbose printing in 'NO_QUERIES' mode, to // avoid needing to call `predicates_of`. This should diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs index 3128e0f31384..dfc800c8e7e1 100644 --- a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.rs @@ -18,4 +18,6 @@ fn main() { //~^ ERROR the size for values of type `V` cannot be known at compilation time bar(); + //~^ ERROR the size for values of type `V` cannot be known at compilation time + //~| ERROR `V` is not an iterator } diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr index 3af115d897d1..6480e490e8b3 100644 --- a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr @@ -23,13 +23,13 @@ LL | where LL | for V: Sized, | ^^^^^ required by this bound in `foo` -error[E0277]: the size for values of type `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` cannot be known at compilation time +error[E0277]: the size for values of type `V` cannot be known at compilation time --> $DIR/bad-sized-cond.rs:20:5 | LL | bar(); | ^^^ doesn't have a size known at compile-time | - = help: the trait `Sized` is not implemented for `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` + = help: the trait `Sized` is not implemented for `V` = note: required for `V` to implement `IntoIterator` note: required by a bound in `bar` --> $DIR/bad-sized-cond.rs:12:15 @@ -40,13 +40,13 @@ LL | where LL | for V: IntoIterator, | ^^^^^^^^^^^^ required by this bound in `bar` -error[E0277]: `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` is not an iterator +error[E0277]: `V` is not an iterator --> $DIR/bad-sized-cond.rs:20:5 | LL | bar(); - | ^^^ `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` is not an iterator + | ^^^ `V` is not an iterator | - = help: the trait `Iterator` is not implemented for `Placeholder(Placeholder { universe: U3, name: Param(DefId(0:6 ~ bad_sized_cond[9450]::bar::V), "V") })` + = help: the trait `Iterator` is not implemented for `V` = note: required for `V` to implement `IntoIterator` note: required by a bound in `bar` --> $DIR/bad-sized-cond.rs:12:15 From d42a3fbd69e5051c803d37d47fc91f41deacf069 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 17 Feb 2023 17:16:43 +0000 Subject: [PATCH 31/67] Assume we can normalize trait default method RPITITs in param-env instead --- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 22 ++++++- compiler/rustc_ty_utils/src/ty.rs | 61 ++++++++++++++++++- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 66c3904af963..5743f086f89b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1599,7 +1599,7 @@ fn check_return_position_impl_trait_in_trait_bounds<'tcx>( { for arg in fn_output.walk() { if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, proj) = ty.kind() + && let ty::Alias(ty::Opaque, proj) = ty.kind() && tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder && tcx.impl_trait_in_trait_parent(proj.def_id) == fn_def_id.to_def_id() { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a34ee1a99a17..ca46cf29919f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -4,7 +4,7 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::ty::layout::IntegerExt; use crate::ty::{ - self, ir::TypeFolder, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, + self, ir::TypeFolder, DefIdTree, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, }; use crate::ty::{GenericArgKind, SubstsRef}; @@ -865,6 +865,26 @@ impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { } t } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if let ty::PredicateKind::Clause(clause) = p.kind().skip_binder() + && let ty::Clause::Projection(projection_pred) = clause + { + p.kind() + .rebind(ty::ProjectionPredicate { + projection_ty: projection_pred.projection_ty.fold_with(self), + // Don't fold the term on the RHS of the projection predicate. + // This is because for default trait methods with RPITITs, we + // install a `NormalizesTo(Projection(RPITIT) -> Opaque(RPITIT))` + // predicate, which would trivially cause a cycle when we do + // anything that requires `ParamEnv::with_reveal_all_normalized`. + term: projection_pred.term, + }) + .to_predicate(self.tcx) + } else { + p.super_fold_with(self) + } + } } impl<'tcx> Ty<'tcx> { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 2c50b766d21f..f1af0073e4da 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,8 +1,12 @@ -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; +#[cfg(not(bootstrap))] +use rustc_middle::ty::ir::TypeVisitable; use rustc_middle::ty::{ - self, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, + self, ir::TypeVisitor, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, + TypeSuperVisitable, }; use rustc_session::config::TraitSolver; use rustc_span::def_id::{DefId, CRATE_DEF_ID}; @@ -136,6 +140,19 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { predicates.extend(environment); } + if tcx.def_kind(def_id) == DefKind::AssocFn + && tcx.associated_item(def_id).container == ty::AssocItemContainer::TraitContainer + { + let sig = tcx.fn_sig(def_id).subst_identity(); + sig.visit_with(&mut ImplTraitInTraitFinder { + tcx, + fn_def_id: def_id, + bound_vars: sig.bound_vars(), + predicates: &mut predicates, + seen: FxHashSet::default(), + }); + } + let local_did = def_id.as_local(); let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)); @@ -222,6 +239,46 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { traits::normalize_param_env_or_error(tcx, unnormalized_env, cause) } +/// Walk through a function type, gathering all RPITITs and installing a +/// `NormalizesTo(Projection(RPITIT) -> Opaque(RPITIT))` predicate into the +/// predicates list. This allows us to observe that an RPITIT projects to +/// its corresponding opaque within the body of a default-body trait method. +struct ImplTraitInTraitFinder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + predicates: &'a mut Vec>, + fn_def_id: DefId, + bound_vars: &'tcx ty::List, + seen: FxHashSet, +} + +impl<'tcx> TypeVisitor> for ImplTraitInTraitFinder<'_, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow { + if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() + && self.tcx.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder + && self.tcx.impl_trait_in_trait_parent(alias_ty.def_id) == self.fn_def_id + && self.seen.insert(alias_ty.def_id) + { + self.predicates.push( + ty::Binder::bind_with_vars( + ty::ProjectionPredicate { + projection_ty: alias_ty, + term: self.tcx.mk_alias(ty::Opaque, alias_ty).into(), + }, + self.bound_vars, + ) + .to_predicate(self.tcx), + ); + + for bound in self.tcx.item_bounds(alias_ty.def_id).subst_iter(self.tcx, alias_ty.substs) + { + bound.visit_with(self); + } + } + + ty.super_visit_with(self) + } +} + /// Elaborate the environment. /// /// Collect a list of `Predicate`'s used for building the `ParamEnv`. Adds `TypeWellFormedFromEnv`'s From 3e57b203918bc64e05c95849672ba7c23090c984 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 23:48:26 +0000 Subject: [PATCH 32/67] Add test --- .../in-trait/async-default-fn-overridden.rs | 66 +++++++++++++++++++ .../async-default-fn-overridden.stderr | 11 ++++ 2 files changed, 77 insertions(+) create mode 100644 tests/ui/async-await/in-trait/async-default-fn-overridden.rs create mode 100644 tests/ui/async-await/in-trait/async-default-fn-overridden.stderr diff --git a/tests/ui/async-await/in-trait/async-default-fn-overridden.rs b/tests/ui/async-await/in-trait/async-default-fn-overridden.rs new file mode 100644 index 000000000000..0fd1a2703db9 --- /dev/null +++ b/tests/ui/async-await/in-trait/async-default-fn-overridden.rs @@ -0,0 +1,66 @@ +// run-pass +// edition:2021 + +#![feature(async_fn_in_trait)] +//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use + +use std::future::Future; + +trait AsyncTrait { + async fn default_impl() { + assert!(false); + } + + async fn call_default_impl() { + Self::default_impl().await + } +} + +struct AsyncType; + +impl AsyncTrait for AsyncType { + async fn default_impl() { + // :) + } +} + +async fn async_main() { + // Should not assert false + AsyncType::call_default_impl().await; +} + +// ------------------------------------------------------------------------- // +// Implementation Details Below... + +use std::pin::Pin; +use std::task::*; + +pub fn noop_waker() -> Waker { + let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE); + + // SAFETY: the contracts for RawWaker and RawWakerVTable are upheld + unsafe { Waker::from_raw(raw) } +} + +const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); + +unsafe fn noop_clone(_p: *const ()) -> RawWaker { + RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE) +} + +unsafe fn noop(_p: *const ()) {} + +fn main() { + let mut fut = async_main(); + + // Poll loop, just to test the future... + let waker = noop_waker(); + let ctx = &mut Context::from_waker(&waker); + + loop { + match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } { + Poll::Pending => {} + Poll::Ready(()) => break, + } + } +} diff --git a/tests/ui/async-await/in-trait/async-default-fn-overridden.stderr b/tests/ui/async-await/in-trait/async-default-fn-overridden.stderr new file mode 100644 index 000000000000..61a826258d09 --- /dev/null +++ b/tests/ui/async-await/in-trait/async-default-fn-overridden.stderr @@ -0,0 +1,11 @@ +warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/async-default-fn-overridden.rs:4:12 + | +LL | #![feature(async_fn_in_trait)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #91611 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + From 4bd2ebc58bed1ee5bf7ff934999e3db3c6e9f22d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 8 Feb 2023 21:17:29 +0000 Subject: [PATCH 33/67] Do not codegen overflow check when not required. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9af408646ae0..a7e74279a13a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -568,8 +568,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // NOTE: Unlike binops, negation doesn't have its own // checked operation, just a comparison with the minimum // value, so we have to check for the assert message. - if !bx.check_overflow() { - if let AssertKind::OverflowNeg(_) = *msg { + if !bx.cx().check_overflow() { + if let AssertKind::OverflowNeg(_) + | AssertKind::Overflow( + mir::BinOp::Add + | mir::BinOp::Sub + | mir::BinOp::Mul + | mir::BinOp::Shl + | mir::BinOp::Shr, + .., + ) = *msg + { const_cond = Some(expected); } } From e34caaf42dd10b3cb8d14e6e99c2c8687fe6342e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 8 Feb 2023 20:28:39 +0000 Subject: [PATCH 34/67] Remove overflow checks from ConstProp. --- .../rustc_mir_transform/src/const_prop.rs | 111 +++--------------- .../src/dataflow_const_prop.rs | 7 +- .../bad_op_div_by_zero.main.ConstProp.diff | 3 +- .../inherit_overflow.main.ConstProp.diff | 39 ++++++ tests/mir-opt/const_prop/inherit_overflow.rs | 9 ++ .../checked.main.DataflowConstProp.diff | 2 +- ...herit_overflow.main.DataflowConstProp.diff | 26 ++-- .../dataflow-const-prop/inherit_overflow.rs | 7 +- 8 files changed, 88 insertions(+), 116 deletions(-) create mode 100644 tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.diff create mode 100644 tests/mir-opt/const_prop/inherit_overflow.rs diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 13f064aa72e5..33ee90ffc119 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -15,7 +15,7 @@ use rustc_middle::mir::visit::{ }; use rustc_middle::mir::{ BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, Location, - Operand, Place, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp, + Operand, Place, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, RETURN_PLACE, }; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; @@ -503,55 +503,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn check_unary_op(&mut self, op: UnOp, arg: &Operand<'tcx>) -> Option<()> { - if self.use_ecx(|this| { - let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?; - let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, &val)?; - Ok(overflow) - })? { - // `AssertKind` only has an `OverflowNeg` variant, so make sure that is - // appropriate to use. - assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); - return None; - } - - Some(()) - } - - fn check_binary_op( - &mut self, - op: BinOp, - left: &Operand<'tcx>, - right: &Operand<'tcx>, - ) -> Option<()> { - let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?)); - let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)); - // Check for exceeding shifts *even if* we cannot evaluate the LHS. - if matches!(op, BinOp::Shr | BinOp::Shl) { - let r = r.clone()?; - // We need the type of the LHS. We cannot use `place_layout` as that is the type - // of the result, which for checked binops is not the same! - let left_ty = left.ty(self.local_decls, self.tcx); - let left_size = self.ecx.layout_of(left_ty).ok()?.size; - let right_size = r.layout.size; - let r_bits = r.to_scalar().to_bits(right_size).ok(); - if r_bits.map_or(false, |b| b >= left_size.bits() as u128) { - return None; - } - } - - if let (Some(l), Some(r)) = (&l, &r) { - // The remaining operators are handled through `overflowing_binary_op`. - if self.use_ecx(|this| { - let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; - Ok(overflow) - })? { - return None; - } - } - Some(()) - } - fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) { match *operand { Operand::Copy(l) | Operand::Move(l) => { @@ -587,28 +538,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // 2. Working around bugs in other parts of the compiler // - In this case, we'll return `None` from this function to stop evaluation. match rvalue { - // Additional checking: give lints to the user if an overflow would occur. - // We do this here and not in the `Assert` terminator as that terminator is - // only sometimes emitted (overflow checks can be disabled), but we want to always - // lint. - Rvalue::UnaryOp(op, arg) => { - trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); - self.check_unary_op(*op, arg)?; - } - Rvalue::BinaryOp(op, box (left, right)) => { - trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); - self.check_binary_op(*op, left, right)?; - } - Rvalue::CheckedBinaryOp(op, box (left, right)) => { - trace!( - "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})", - op, - left, - right - ); - self.check_binary_op(*op, left, right)?; - } - // Do not try creating references (#67862) Rvalue::AddressOf(_, place) | Rvalue::Ref(_, _, place) => { trace!("skipping AddressOf | Ref for {:?}", place); @@ -638,7 +567,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) - | Rvalue::NullaryOp(..) => {} + | Rvalue::NullaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::BinaryOp(..) + | Rvalue::CheckedBinaryOp(..) => {} } // FIXME we need to revisit this for #67176 @@ -1079,31 +1011,18 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { // Do NOT early return in this function, it does some crucial fixup of the state at the end! match &mut terminator.kind { TerminatorKind::Assert { expected, ref mut cond, .. } => { - if let Some(ref value) = self.eval_operand(&cond) { - trace!("assertion on {:?} should be {:?}", value, expected); - let expected = Scalar::from_bool(*expected); + if let Some(ref value) = self.eval_operand(&cond) // FIXME should be used use_ecx rather than a local match... but we have // quite a few of these read_scalar/read_immediate that need fixing. - if let Ok(value_const) = self.ecx.read_scalar(&value) { - if expected != value_const { - // Poison all places this operand references so that further code - // doesn't use the invalid value - match cond { - Operand::Move(ref place) | Operand::Copy(ref place) => { - Self::remove_const(&mut self.ecx, place.local); - } - Operand::Constant(_) => {} - } - } else { - if self.should_const_prop(value) { - *cond = self.operand_from_scalar( - value_const, - self.tcx.types.bool, - source_info.span, - ); - } - } - } + && let Ok(value_const) = self.ecx.read_scalar(&value) + && self.should_const_prop(value) + { + trace!("assertion on {:?} should be {:?}", value, expected); + *cond = self.operand_from_scalar( + value_const, + self.tcx.types.bool, + source_info.span, + ); } } TerminatorKind::SwitchInt { ref mut discr, .. } => { diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index f3ca2337e59c..19019e3ef744 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -180,12 +180,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { let overflow = match overflow { FlatSet::Top => FlatSet::Top, FlatSet::Elem(overflow) => { - if overflow { - // Overflow cannot be reliably propagated. See: https://github.com/rust-lang/rust/pull/101168#issuecomment-1288091446 - FlatSet::Top - } else { - self.wrap_scalar(Scalar::from_bool(false), self.tcx.types.bool) - } + self.wrap_scalar(Scalar::from_bool(overflow), self.tcx.types.bool) } FlatSet::Bottom => FlatSet::Bottom, }; diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff index bea32a67ef4a..900061a484b3 100644 --- a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff +++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff @@ -24,9 +24,10 @@ StorageLive(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:18: +2:19 - _3 = _1; // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:18: +2:19 - _4 = Eq(_3, const 0_i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:14: +2:19 +- assert(!move _4, "attempt to divide `{}` by zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:14: +2:19 + _3 = const 0_i32; // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:18: +2:19 + _4 = const true; // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:14: +2:19 - assert(!move _4, "attempt to divide `{}` by zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:14: +2:19 ++ assert(!const true, "attempt to divide `{}` by zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:+2:14: +2:19 } bb1: { diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.diff new file mode 100644 index 000000000000..d03c23a3fb56 --- /dev/null +++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.diff @@ -0,0 +1,39 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inherit_overflow.rs:+0:11: +0:11 + let mut _1: u8; // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + let mut _2: u8; // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + let mut _3: u8; // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + scope 1 { + } + scope 2 (inlined ::add) { // at $DIR/inherit_overflow.rs:8:13: 8:47 + debug self => _2; // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + debug other => _3; // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + let mut _4: (u8, bool); // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + StorageLive(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + _2 = const u8::MAX; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + StorageLive(_3); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + _3 = const 1_u8; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 +- _4 = CheckedAdd(_2, _3); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL +- assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL ++ _4 = const (0_u8, true); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL ++ assert(!const true, "attempt to compute `{} + {}`, which would overflow", _2, _3) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + } + + bb1: { +- _1 = move (_4.0: u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL ++ _1 = const 0_u8; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + StorageDead(_3); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + StorageDead(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + StorageDead(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:47: +3:48 + _0 = const (); // scope 0 at $DIR/inherit_overflow.rs:+0:11: +4:2 + return; // scope 0 at $DIR/inherit_overflow.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/const_prop/inherit_overflow.rs b/tests/mir-opt/const_prop/inherit_overflow.rs new file mode 100644 index 000000000000..541a8c5c3af2 --- /dev/null +++ b/tests/mir-opt/const_prop/inherit_overflow.rs @@ -0,0 +1,9 @@ +// unit-test: ConstProp +// compile-flags: -Zmir-enable-passes=+Inline + +// EMIT_MIR inherit_overflow.main.ConstProp.diff +fn main() { + // After inlining, this will contain a `CheckedBinaryOp`. + // Propagating the overflow is ok as codegen will just skip emitting the panic. + let _ = ::add(255, 1); +} diff --git a/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff index a4ebd0c8c18f..944afed8f465 100644 --- a/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff +++ b/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff @@ -61,7 +61,7 @@ - assert(!move (_10.1: bool), "attempt to compute `{} + {}`, which would overflow", move _9, const 1_i32) -> bb2; // scope 4 at $DIR/checked.rs:+6:13: +6:18 + _9 = const i32::MAX; // scope 4 at $DIR/checked.rs:+6:13: +6:14 + _10 = CheckedAdd(const i32::MAX, const 1_i32); // scope 4 at $DIR/checked.rs:+6:13: +6:18 -+ assert(!move (_10.1: bool), "attempt to compute `{} + {}`, which would overflow", const i32::MAX, const 1_i32) -> bb2; // scope 4 at $DIR/checked.rs:+6:13: +6:18 ++ assert(!const true, "attempt to compute `{} + {}`, which would overflow", const i32::MAX, const 1_i32) -> bb2; // scope 4 at $DIR/checked.rs:+6:13: +6:18 } bb2: { diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff index 33122f465fe4..29781e9ce188 100644 --- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff +++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff @@ -5,26 +5,34 @@ let mut _0: (); // return place in scope 0 at $DIR/inherit_overflow.rs:+0:11: +0:11 let mut _1: u8; // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 let mut _2: u8; // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + let mut _3: u8; // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 scope 1 { } - scope 2 (inlined ::add) { // at $DIR/inherit_overflow.rs:7:13: 7:47 - debug self => _1; // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL - debug other => _2; // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL - let mut _3: (u8, bool); // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + scope 2 (inlined ::add) { // at $DIR/inherit_overflow.rs:8:13: 8:47 + debug self => _2; // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + debug other => _3; // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + let mut _4: (u8, bool); // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL } bb0: { StorageLive(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 - _1 = const u8::MAX; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 StorageLive(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 - _2 = const 1_u8; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 - _3 = CheckedAdd(const u8::MAX, const 1_u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL - assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + _2 = const u8::MAX; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + StorageLive(_3); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + _3 = const 1_u8; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 +- _4 = CheckedAdd(_2, _3); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL +- assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL ++ _4 = CheckedAdd(const u8::MAX, const 1_u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL ++ assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL } bb1: { +- _1 = move (_4.0: u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL ++ _1 = const 0_u8; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL + StorageDead(_3); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 StorageDead(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 - StorageDead(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47 + StorageDead(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:47: +3:48 + _0 = const (); // scope 0 at $DIR/inherit_overflow.rs:+0:11: +4:2 return; // scope 0 at $DIR/inherit_overflow.rs:+4:2: +4:2 } } diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs index 2f2d9d0102d1..f4aba60f0c80 100644 --- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs +++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs @@ -1,8 +1,9 @@ -// compile-flags: -Zunsound-mir-opts +// unit-test: DataflowConstProp +// compile-flags: -Zmir-enable-passes=+Inline // EMIT_MIR inherit_overflow.main.DataflowConstProp.diff fn main() { - // After inlining, this will contain a `CheckedBinaryOp`. The overflow - // must be ignored by the constant propagation to avoid triggering a panic. + // After inlining, this will contain a `CheckedBinaryOp`. + // Propagating the overflow is ok as codegen will just skip emitting the panic. let _ = ::add(255, 1); } From 86dbcb53907037dcc3fabb635956aec2c384b4e9 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 08:59:19 +0000 Subject: [PATCH 35/67] Add codegen test. --- tests/codegen/inherit_overflow.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/codegen/inherit_overflow.rs diff --git a/tests/codegen/inherit_overflow.rs b/tests/codegen/inherit_overflow.rs new file mode 100644 index 000000000000..d938dadb9c75 --- /dev/null +++ b/tests/codegen/inherit_overflow.rs @@ -0,0 +1,14 @@ +// compile-flags: -Zmir-enable-passes=+Inline,+ConstProp --crate-type lib +// revisions: ASSERT NOASSERT +//[ASSERT] compile-flags: -Coverflow-checks=on +//[NOASSERT] compile-flags: -Coverflow-checks=off + +// CHECK-LABEL: define{{.*}} @assertion +// ASSERT: tail call void @_ZN4core9panicking5panic17h +// NOASSERT: ret i8 0 +#[no_mangle] +pub fn assertion() -> u8 { + // Optimized MIR will replace this `CheckedBinaryOp` by `const (0, true)`. + // Verify that codegen does or does not emit the panic. + ::add(255, 1) +} From d0cc00f7580d154aa8d9edd777a5aa7e6e6cfd22 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 14:56:06 +0000 Subject: [PATCH 36/67] Add comment. --- compiler/rustc_middle/src/mir/syntax.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 66ee68187896..2e71fc198688 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -671,6 +671,9 @@ pub enum TerminatorKind<'tcx> { /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the /// assertion does not fail, execution continues at the specified basic block. + /// + /// When overflow checking is disabled and we are generating run-time code, the `Overflow*` + /// variants of this terminator are codegened as simple `goto target`. Assert { cond: Operand<'tcx>, expected: bool, From 1480b1c524cad3a7ee0253496b7b026484e53371 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 16:38:42 +0000 Subject: [PATCH 37/67] Correct comment. --- compiler/rustc_middle/src/mir/syntax.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2e71fc198688..badf33293ad4 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -672,8 +672,10 @@ pub enum TerminatorKind<'tcx> { /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the /// assertion does not fail, execution continues at the specified basic block. /// - /// When overflow checking is disabled and we are generating run-time code, the `Overflow*` - /// variants of this terminator are codegened as simple `goto target`. + /// When overflow checking is disabled and this is run-time MIR (as opposed to compile-time MIR + /// that is used for CTFE), the following variants of this terminator behave as `goto target`: + /// - `OverflowNeg(..)`, + /// - `Overflow(op, ..)` if op is a checkable operation (add, sub, mul, shl, shr). Assert { cond: Operand<'tcx>, expected: bool, From 7567f1f31d5e968380b1e1c5088b00a0606ea1a0 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 16:41:37 +0000 Subject: [PATCH 38/67] Adapt interpreter. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 16 ++++++---------- .../rustc_const_eval/src/interpret/terminator.rs | 8 +++++++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index a7e74279a13a..35f12ac9dbd7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -569,16 +569,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // checked operation, just a comparison with the minimum // value, so we have to check for the assert message. if !bx.cx().check_overflow() { - if let AssertKind::OverflowNeg(_) - | AssertKind::Overflow( - mir::BinOp::Add - | mir::BinOp::Sub - | mir::BinOp::Mul - | mir::BinOp::Shl - | mir::BinOp::Shr, - .., - ) = *msg - { + let unchecked_overflow = match msg { + AssertKind::OverflowNeg(..) => true, + AssertKind::Overflow(op, ..) => op.is_checkable(), + _ => false, + }; + if unchecked_overflow { const_cond = Some(expected); } } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index da320cd1cd5f..919eaca90ce6 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -137,8 +137,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Assert { ref cond, expected, ref msg, target, cleanup } => { + let ignored = !self.tcx.sess.overflow_checks() + && match msg { + mir::AssertKind::OverflowNeg(..) => true, + mir::AssertKind::Overflow(op, ..) => op.is_checkable(), + _ => false, + }; let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?; - if expected == cond_val { + if ignored || expected == cond_val { self.go_to_block(target); } else { M::assert_panic(self, msg, cleanup)?; From 4c93145b3304f7d1f4ec7ac284595f3505f4de72 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 16:42:19 +0000 Subject: [PATCH 39/67] Remove outdated comment. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 35f12ac9dbd7..7babb357c7c0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -565,9 +565,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // with #[rustc_inherit_overflow_checks] and inlined from // another crate (mostly core::num generic/#[inline] fns), // while the current crate doesn't use overflow checks. - // NOTE: Unlike binops, negation doesn't have its own - // checked operation, just a comparison with the minimum - // value, so we have to check for the assert message. if !bx.cx().check_overflow() { let unchecked_overflow = match msg { AssertKind::OverflowNeg(..) => true, From 7f36a3fcd788292c56c3a21e6145e7f6c78f0d84 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 17:07:36 +0000 Subject: [PATCH 40/67] Fix CTFE interpreter. --- compiler/rustc_const_eval/src/interpret/terminator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 919eaca90ce6..d9a390184d96 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -137,7 +137,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Assert { ref cond, expected, ref msg, target, cleanup } => { - let ignored = !self.tcx.sess.overflow_checks() + let ignored = M::checked_binop_checks_overflow(self) && match msg { mir::AssertKind::OverflowNeg(..) => true, mir::AssertKind::Overflow(op, ..) => op.is_checkable(), From 085eaa7ee303d487dc254bca2a402284450e163e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 17:12:57 +0000 Subject: [PATCH 41/67] Adapt interpreter. --- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 4 +--- compiler/rustc_const_eval/src/interpret/operator.rs | 8 -------- compiler/rustc_const_eval/src/interpret/step.rs | 4 +--- compiler/rustc_const_eval/src/interpret/terminator.rs | 2 +- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 8877bb479d48..d18d9bb5a986 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -219,9 +219,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::mul_with_overflow => BinOp::Mul, _ => bug!(), }; - self.binop_with_overflow( - bin_op, /*force_overflow_checks*/ true, &lhs, &rhs, dest, - )?; + self.binop_with_overflow(bin_op, &lhs, &rhs, dest)?; } sym::saturating_add | sym::saturating_sub => { let l = self.read_immediate(&args[0])?; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index e8ff70e3a409..422120084d31 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -10,13 +10,9 @@ use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Applies the binary operation `op` to the two operands and writes a tuple of the result /// and a boolean signifying the potential overflow to the destination. - /// - /// `force_overflow_checks` indicates whether overflow checks should be done even when - /// `tcx.sess.overflow_checks()` is `false`. pub fn binop_with_overflow( &mut self, op: mir::BinOp, - force_overflow_checks: bool, left: &ImmTy<'tcx, M::Provenance>, right: &ImmTy<'tcx, M::Provenance>, dest: &PlaceTy<'tcx, M::Provenance>, @@ -28,10 +24,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { "type mismatch for result of {:?}", op, ); - // As per https://github.com/rust-lang/rust/pull/98738, we always return `false` in the 2nd - // component when overflow checking is disabled. - let overflowed = - overflowed && (force_overflow_checks || M::checked_binop_checks_overflow(self)); // Write the result to `dest`. if let Abi::ScalarPair(..) = dest.layout.abi { // We can use the optimized path and avoid `place_field` (which might do diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 8252e73c5d93..6863435e5087 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -185,9 +185,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let left = self.read_immediate(&self.eval_operand(left, None)?)?; let layout = binop_right_homogeneous(bin_op).then_some(left.layout); let right = self.read_immediate(&self.eval_operand(right, layout)?)?; - self.binop_with_overflow( - bin_op, /*force_overflow_checks*/ false, &left, &right, &dest, - )?; + self.binop_with_overflow(bin_op, &left, &right, &dest)?; } UnaryOp(un_op, ref operand) => { diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index d9a390184d96..70805972c406 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -137,7 +137,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Assert { ref cond, expected, ref msg, target, cleanup } => { - let ignored = M::checked_binop_checks_overflow(self) + let ignored = !M::checked_binop_checks_overflow(self) && match msg { mir::AssertKind::OverflowNeg(..) => true, mir::AssertKind::Overflow(op, ..) => op.is_checkable(), From fb1f7f747a48a87015a46e90814619a4efd12d75 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 17:14:37 +0000 Subject: [PATCH 42/67] Remove exception from MIR doc. --- compiler/rustc_middle/src/mir/syntax.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index badf33293ad4..288488e45823 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1108,10 +1108,6 @@ pub enum Rvalue<'tcx> { /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. /// - /// When overflow checking is disabled and we are generating run-time code, the error condition - /// is false. Otherwise, and always during CTFE, the error condition is determined as described - /// below. - /// /// For addition, subtraction, and multiplication on integers the error condition is set when /// the infinite precision result would be unequal to the actual result. /// From 328696ca17551b94fe2cfac0d76d51bd4f28bc87 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 11 Feb 2023 18:21:31 +0100 Subject: [PATCH 43/67] Update compiler/rustc_middle/src/mir/syntax.rs Co-authored-by: Ralf Jung --- compiler/rustc_middle/src/mir/syntax.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 288488e45823..ae09562a85e9 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -675,7 +675,8 @@ pub enum TerminatorKind<'tcx> { /// When overflow checking is disabled and this is run-time MIR (as opposed to compile-time MIR /// that is used for CTFE), the following variants of this terminator behave as `goto target`: /// - `OverflowNeg(..)`, - /// - `Overflow(op, ..)` if op is a checkable operation (add, sub, mul, shl, shr). + /// - `Overflow(op, ..)` if op is a "checkable" operation (add, sub, mul, shl, shr, but NOT + /// div or rem). Assert { cond: Operand<'tcx>, expected: bool, From 4f13aa7f46630b92ac6148e006fb553e712f7d27 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 17:26:20 +0000 Subject: [PATCH 44/67] Comment codegen optimization. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 7c8d23a3329c..95bc0b30bea8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -656,6 +656,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // with #[rustc_inherit_overflow_checks] and inlined from // another crate (mostly core::num generic/#[inline] fns), // while the current crate doesn't use overflow checks. + // + // As an optimization, we emit an unchecked binop when overflow checks are disabled, + // as the overflow flag would be ignored anyway. if !bx.cx().check_overflow() { let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty); return OperandValue::Pair(val, bx.cx().const_bool(false)); From e9c73ea502d789fc103e012c052e25cf55f653e3 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 17:29:12 +0000 Subject: [PATCH 45/67] Make name more explicit. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 7babb357c7c0..7d16964abfde 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -566,12 +566,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // another crate (mostly core::num generic/#[inline] fns), // while the current crate doesn't use overflow checks. if !bx.cx().check_overflow() { - let unchecked_overflow = match msg { + let overflow_not_to_check = match msg { AssertKind::OverflowNeg(..) => true, AssertKind::Overflow(op, ..) => op.is_checkable(), _ => false, }; - if unchecked_overflow { + if overflow_not_to_check { const_cond = Some(expected); } } From a5769193d1661238b4792c26daef350da1df5cf6 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 11 Feb 2023 21:01:11 +0000 Subject: [PATCH 46/67] Remove special case in rvalue codegen. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 95bc0b30bea8..41cd1c09a4e7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -652,18 +652,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { rhs: Bx::Value, input_ty: Ty<'tcx>, ) -> OperandValue { - // This case can currently arise only from functions marked - // with #[rustc_inherit_overflow_checks] and inlined from - // another crate (mostly core::num generic/#[inline] fns), - // while the current crate doesn't use overflow checks. - // - // As an optimization, we emit an unchecked binop when overflow checks are disabled, - // as the overflow flag would be ignored anyway. - if !bx.cx().check_overflow() { - let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty); - return OperandValue::Pair(val, bx.cx().const_bool(false)); - } - let (val, of) = match op { // These are checked using intrinsics mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { From c1a0f8205ca17ca60ffb332b2d4994e22d09cb6e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 12 Feb 2023 15:31:34 +0000 Subject: [PATCH 47/67] Rename checked_binop_checks_overflow. --- compiler/rustc_const_eval/src/interpret/machine.rs | 9 +++++---- compiler/rustc_const_eval/src/interpret/terminator.rs | 2 +- src/tools/miri/src/machine.rs | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 5b7b0dc66d18..450488315ef0 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -147,8 +147,9 @@ pub trait Machine<'mir, 'tcx>: Sized { true } - /// Whether CheckedBinOp MIR statements should actually check for overflow. - fn checked_binop_checks_overflow(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Whether Assert(OverflowNeg) and Assert(Overflow) MIR terminators should actually + /// check for overflow. + fn ignore_checkable_overflow_assertions(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; /// Entry point for obtaining the MIR of anything that should get evaluated. /// So not just functions and shims, but also const/static initializers, anonymous @@ -466,8 +467,8 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn checked_binop_checks_overflow(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { - true + fn ignore_checkable_overflow_assertions(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + false } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 70805972c406..294bd35c13ee 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -137,7 +137,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Assert { ref cond, expected, ref msg, target, cleanup } => { - let ignored = !M::checked_binop_checks_overflow(self) + let ignored = M::ignore_checkable_overflow_assertions(self) && match msg { mir::AssertKind::OverflowNeg(..) => true, mir::AssertKind::Overflow(op, ..) => op.is_checkable(), diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index c99cc68dbab4..8bd1e802f8a5 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -815,8 +815,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } #[inline(always)] - fn checked_binop_checks_overflow(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { - ecx.tcx.sess.overflow_checks() + fn ignore_checkable_overflow_assertions(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { + !ecx.tcx.sess.overflow_checks() } #[inline(always)] From c107e0e9454555505475758893a88829cdde03f6 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 15 Feb 2023 18:43:15 +0000 Subject: [PATCH 48/67] Fix codegen test. --- tests/codegen/inherit_overflow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/inherit_overflow.rs b/tests/codegen/inherit_overflow.rs index d938dadb9c75..0b0b890b2c99 100644 --- a/tests/codegen/inherit_overflow.rs +++ b/tests/codegen/inherit_overflow.rs @@ -4,7 +4,7 @@ //[NOASSERT] compile-flags: -Coverflow-checks=off // CHECK-LABEL: define{{.*}} @assertion -// ASSERT: tail call void @_ZN4core9panicking5panic17h +// ASSERT: call void @_ZN4core9panicking5panic17h // NOASSERT: ret i8 0 #[no_mangle] pub fn assertion() -> u8 { From f79db599531d6e9cc651eb9ab22fc650782b70d3 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 15 Feb 2023 18:46:56 +0000 Subject: [PATCH 49/67] Adapt cg_clif. --- compiler/rustc_codegen_cranelift/src/base.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 95778de3abaf..7f857528c7c5 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -347,7 +347,12 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { } TerminatorKind::Assert { cond, expected, msg, target, cleanup: _ } => { if !fx.tcx.sess.overflow_checks() { - if let mir::AssertKind::OverflowNeg(_) = *msg { + let overflow_not_to_check = match msg { + AssertKind::OverflowNeg(..) => true, + AssertKind::Overflow(op, ..) => op.is_checkable(), + _ => false, + }; + if overflow_not_to_check { let target = fx.get_block(*target); fx.bcx.ins().jump(target, &[]); continue; @@ -567,15 +572,7 @@ fn codegen_stmt<'tcx>( let lhs = codegen_operand(fx, &lhs_rhs.0); let rhs = codegen_operand(fx, &lhs_rhs.1); - let res = if !fx.tcx.sess.overflow_checks() { - let val = - crate::num::codegen_int_binop(fx, bin_op, lhs, rhs).load_scalar(fx); - let is_overflow = fx.bcx.ins().iconst(types::I8, 0); - CValue::by_val_pair(val, is_overflow, lval.layout()) - } else { - crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs) - }; - + let res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); lval.write_cvalue(fx, res); } Rvalue::UnaryOp(un_op, ref operand) => { From 7e795bdf03bd75cb9e502f5cdeac693da2f24dc7 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 18 Feb 2023 21:45:10 +0000 Subject: [PATCH 50/67] Replace _with_overflow instrinsics in LowerIntrinsics. --- .../src/lower_intrinsics.rs | 26 +++++- tests/mir-opt/lower_intrinsics.rs | 7 ++ ...rinsics.with_overflow.LowerIntrinsics.diff | 83 +++++++++++++++++++ 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 9892580e63dc..f596cc1808fa 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -107,9 +107,29 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { } } sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { - // The checked binary operations are not suitable target for lowering here, - // since their semantics depend on the value of overflow-checks flag used - // during codegen. Issue #35310. + if let Some(target) = *target { + let lhs; + let rhs; + { + let mut args = args.drain(..); + lhs = args.next().unwrap(); + rhs = args.next().unwrap(); + } + let bin_op = match intrinsic_name { + sym::add_with_overflow => BinOp::Add, + sym::sub_with_overflow => BinOp::Sub, + sym::mul_with_overflow => BinOp::Mul, + _ => bug!("unexpected intrinsic"), + }; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::CheckedBinaryOp(bin_op, Box::new((lhs, rhs))), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } } sym::size_of | sym::min_align_of => { if let Some(target) = *target { diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 66dae0e46b9d..7147be43ca5e 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -72,3 +72,10 @@ pub fn assume() { std::intrinsics::assume(true); } } + +// EMIT_MIR lower_intrinsics.with_overflow.LowerIntrinsics.diff +pub fn with_overflow(a: i32, b: i32) { + let _x = core::intrinsics::add_with_overflow(a, b); + let _y = core::intrinsics::sub_with_overflow(a, b); + let _z = core::intrinsics::mul_with_overflow(a, b); +} diff --git a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff new file mode 100644 index 000000000000..9870a70dec5e --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff @@ -0,0 +1,83 @@ +- // MIR for `with_overflow` before LowerIntrinsics ++ // MIR for `with_overflow` after LowerIntrinsics + + fn with_overflow(_1: i32, _2: i32) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:22: +0:23 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:+0:30: +0:31 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:38: +0:38 + let _3: (i32, bool); // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let mut _4: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + let mut _5: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 + let mut _7: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + let mut _8: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+2:53: +2:54 + let mut _10: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+3:50: +3:51 + let mut _11: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+3:53: +3:54 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let _6: (i32, bool); // in scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + let _9: (i32, bool); // in scope 2 at $DIR/lower_intrinsics.rs:+3:9: +3:11 + scope 3 { + debug _z => _9; // in scope 3 at $DIR/lower_intrinsics.rs:+3:9: +3:11 + } + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + StorageLive(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 + _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 +- _3 = add_with_overflow::(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:78:14: 78:49 +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {add_with_overflow::}, val: Value() } ++ _3 = CheckedAdd(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 ++ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:54: +1:55 + StorageDead(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:54: +1:55 + StorageLive(_6); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + StorageLive(_7); // scope 1 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + _7 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + StorageLive(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:53: +2:54 + _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:53: +2:54 +- _6 = sub_with_overflow::(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:79:14: 79:49 +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {sub_with_overflow::}, val: Value() } ++ _6 = CheckedSub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 ++ goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 + } + + bb2: { + StorageDead(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:54: +2:55 + StorageDead(_7); // scope 1 at $DIR/lower_intrinsics.rs:+2:54: +2:55 + StorageLive(_9); // scope 2 at $DIR/lower_intrinsics.rs:+3:9: +3:11 + StorageLive(_10); // scope 2 at $DIR/lower_intrinsics.rs:+3:50: +3:51 + _10 = _1; // scope 2 at $DIR/lower_intrinsics.rs:+3:50: +3:51 + StorageLive(_11); // scope 2 at $DIR/lower_intrinsics.rs:+3:53: +3:54 + _11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:+3:53: +3:54 +- _9 = mul_with_overflow::(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:80:14: 80:49 +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {mul_with_overflow::}, val: Value() } ++ _9 = CheckedMul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 ++ goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 + } + + bb3: { + StorageDead(_11); // scope 2 at $DIR/lower_intrinsics.rs:+3:54: +3:55 + StorageDead(_10); // scope 2 at $DIR/lower_intrinsics.rs:+3:54: +3:55 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:38: +4:2 + StorageDead(_9); // scope 2 at $DIR/lower_intrinsics.rs:+4:1: +4:2 + StorageDead(_6); // scope 1 at $DIR/lower_intrinsics.rs:+4:1: +4:2 + StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:+4:1: +4:2 + return; // scope 0 at $DIR/lower_intrinsics.rs:+4:2: +4:2 + } + } + From 9f6c1df8723504d7d468c87af02670dd0067cc7b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 18 Feb 2023 22:06:29 +0000 Subject: [PATCH 51/67] Stop implementing _with_overflow intrinsics in codegen backends. --- .../src/intrinsics/mod.rs | 14 ----------- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 25 ------------------- .../src/interpret/intrinsics.rs | 11 -------- 3 files changed, 50 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 0d2367c2f83d..6feb3a7732e1 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -493,20 +493,6 @@ fn codegen_regular_intrinsic_call<'tcx>( let res = crate::num::codegen_int_binop(fx, bin_op, x, y); ret.write_cvalue(fx, res); } - sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { - intrinsic_args!(fx, args => (x, y); intrinsic); - - assert_eq!(x.layout().ty, y.layout().ty); - let bin_op = match intrinsic { - sym::add_with_overflow => BinOp::Add, - sym::sub_with_overflow => BinOp::Sub, - sym::mul_with_overflow => BinOp::Mul, - _ => unreachable!(), - }; - - let res = crate::num::codegen_checked_int_binop(fx, bin_op, x, y); - ret.write_cvalue(fx, res); - } sym::saturating_add | sym::saturating_sub => { intrinsic_args!(fx, args => (lhs, rhs); intrinsic); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 766dc74cbbb6..7af7fc92dbce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -218,9 +218,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].val.unaligned_volatile_store(bx, dst); return; } - sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow | sym::unchecked_div | sym::unchecked_rem | sym::unchecked_shl @@ -232,28 +229,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = arg_tys[0]; match int_type_width_signed(ty, bx.tcx()) { Some((_width, signed)) => match name { - sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow => { - let op = match name { - sym::add_with_overflow => OverflowOp::Add, - sym::sub_with_overflow => OverflowOp::Sub, - sym::mul_with_overflow => OverflowOp::Mul, - _ => bug!(), - }; - let (val, overflow) = - bx.checked_binop(op, ty, args[0].immediate(), args[1].immediate()); - // Convert `i1` to a `bool`, and write it to the out parameter - let val = bx.from_immediate(val); - let overflow = bx.from_immediate(overflow); - - let dest = result.project_field(bx, 0); - bx.store(val, dest.llval, dest.align); - let dest = result.project_field(bx, 1); - bx.store(overflow, dest.llval, dest.align); - - return; - } sym::exact_div => { if signed { bx.exactsdiv(args[0].immediate(), args[1].immediate()) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d18d9bb5a986..c5d558aeb6cc 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -210,17 +210,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let out_val = numeric_intrinsic(intrinsic_name, bits, kind); self.write_scalar(out_val, dest)?; } - sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { - let lhs = self.read_immediate(&args[0])?; - let rhs = self.read_immediate(&args[1])?; - let bin_op = match intrinsic_name { - sym::add_with_overflow => BinOp::Add, - sym::sub_with_overflow => BinOp::Sub, - sym::mul_with_overflow => BinOp::Mul, - _ => bug!(), - }; - self.binop_with_overflow(bin_op, &lhs, &rhs, dest)?; - } sym::saturating_add | sym::saturating_sub => { let l = self.read_immediate(&args[0])?; let r = self.read_immediate(&args[1])?; From fd62036caa266460bcdee5adc370232751e6eb0d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 16 Feb 2023 18:33:14 +0100 Subject: [PATCH 52/67] Correctly handle if a link starts with a whitespace --- compiler/rustc_resolve/src/rustdoc.rs | 1 + src/librustdoc/passes/collect_intra_doc_links.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 3425e24585cd..74c0527d33ff 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -340,6 +340,7 @@ pub fn inner_docs(attrs: &[ast::Attribute]) -> bool { fn preprocess_link(link: &str) -> String { let link = link.replace('`', ""); let link = link.split('#').next().unwrap(); + let link = link.trim(); let link = link.rsplit('@').next().unwrap(); let link = link.strip_suffix("()").unwrap_or(link); let link = link.strip_suffix("{}").unwrap_or(link); diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index b2208da9060d..097af5f87347 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -884,7 +884,8 @@ fn preprocess_link( let mut parts = stripped.split('#'); let link = parts.next().unwrap(); - if link.trim().is_empty() { + let link = link.trim(); + if link.is_empty() { // This is an anchor to an element of the current page, nothing to do in here! return None; } @@ -897,7 +898,7 @@ fn preprocess_link( // Parse and strip the disambiguator from the link, if present. let (disambiguator, path_str, link_text) = match Disambiguator::from_str(link) { Ok(Some((d, path, link_text))) => (Some(d), path.trim(), link_text.trim()), - Ok(None) => (None, link.trim(), link.trim()), + Ok(None) => (None, link, link), Err((err_msg, relative_range)) => { // Only report error if we would not have ignored this link. See issue #83859. if !should_ignore_link_with_disambiguators(link) { From 8d801fd3983d83ee701beb35b86d3d5b6540890e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 16 Feb 2023 18:33:29 +0100 Subject: [PATCH 53/67] Add regression test for #107995 --- tests/rustdoc/issue-107995.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/rustdoc/issue-107995.rs diff --git a/tests/rustdoc/issue-107995.rs b/tests/rustdoc/issue-107995.rs new file mode 100644 index 000000000000..1273e4fdd12e --- /dev/null +++ b/tests/rustdoc/issue-107995.rs @@ -0,0 +1,28 @@ +// Regression test for . + +#![crate_name = "foo"] + +// @has 'foo/fn.foo.html' +// @has - '//*[@class="docblock"]//a[@href="fn.bar.html"]' 'bar`' +/// A foo, see also [ bar`] +pub fn foo() {} + +// @has 'foo/fn.bar.html' +// @has - '//*[@class="docblock"]' 'line Path line' +// @has - '//*[@class="docblock"]//a[@href="struct.Path.html"]' 'Path' +#[doc = "line ["] +#[doc = "Path"] +#[doc = "] line"] +pub fn bar() {} + +// @has 'foo/fn.another.html' +// @has - '//*[@class="docblock"]//a[@href="struct.Path.html"]' 'Path' +/// [ `Path`] +pub fn another() {} + +// @has 'foo/fn.last.html' +// @has - '//*[@class="docblock"]//a[@href="struct.Path.html"]' 'Path' +/// [ Path`] +pub fn last() {} + +pub struct Path; From cd3d4fcedf8e40b19843e1cc5d2988e452602513 Mon Sep 17 00:00:00 2001 From: KittyBorgX Date: Sat, 18 Feb 2023 19:48:58 +0530 Subject: [PATCH 54/67] Download rustfmt regardless of rustc being set in config.toml --- src/bootstrap/config.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index cd027a4abb7f..56f96734bbbc 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -1315,15 +1315,6 @@ impl Config { } else { RustfmtState::Unavailable }; - } else { - // If using a system toolchain for bootstrapping, see if that has rustfmt available. - let host = config.build; - let rustfmt_path = config.initial_rustc.with_file_name(exe("rustfmt", host)); - let bin_root = config.out.join(host.triple).join("stage0"); - if !rustfmt_path.starts_with(&bin_root) { - // Using a system-provided toolchain; we shouldn't download rustfmt. - *config.initial_rustfmt.borrow_mut() = RustfmtState::SystemToolchain(rustfmt_path); - } } // Now that we've reached the end of our configuration, infer the From b118569268e4b9f47757f0920aab353675af2da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 19 Feb 2023 00:00:00 +0000 Subject: [PATCH 55/67] Use custom implementation of read_buf in Read for &'a FileDesc This allows to skip an unnecessary buffer initialization. --- library/std/src/sys/unix/fd.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index dbaa3c33e2e5..ab57fba6c9f2 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -284,6 +284,10 @@ impl<'a> Read for &'a FileDesc { fn read(&mut self, buf: &mut [u8]) -> io::Result { (**self).read(buf) } + + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } } impl AsInner for FileDesc { From 488d0c9efd355f712db4ea34856ddbeea26a7049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 8 Dec 2022 13:31:21 +0100 Subject: [PATCH 56/67] Type-directed probing for inherent associated types --- .../rustc_hir_analysis/src/astconv/errors.rs | 166 ++++++++++- .../rustc_hir_analysis/src/astconv/mod.rs | 257 ++++++++++++++---- .../dispatch-on-self-type-0.rs | 35 +++ .../dispatch-on-self-type-1.rs | 39 +++ .../dispatch-on-self-type-2.rs | 17 ++ .../dispatch-on-self-type-2.stderr | 19 ++ .../not-found-self-type-differs.alias.stderr | 16 ++ .../not-found-self-type-differs.local.stderr | 16 ++ .../not-found-self-type-differs.rs | 22 ++ .../not-found-unsatisfied-bounds.rs | 21 ++ .../not-found-unsatisfied-bounds.stderr | 27 ++ 11 files changed, 581 insertions(+), 54 deletions(-) create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 232ef2079d6b..191f4f0f910a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -1,10 +1,10 @@ use crate::astconv::AstConv; use crate::errors::{ManualImplementation, MissingTypeParams}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::parse::feature_err; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; @@ -221,6 +221,168 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit() } + // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. + pub(crate) fn complain_about_inherent_assoc_type_not_found( + &self, + name: Ident, + self_ty: Ty<'tcx>, + candidates: &[DefId], + unsatisfied_predicates: Vec>, + span: Span, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + + let adt_did = self_ty.ty_adt_def().map(|def| def.did()); + let add_def_label = |err: &mut Diagnostic| { + if let Some(did) = adt_did { + err.span_label( + tcx.def_span(did), + format!( + "associated item `{name}` not found for this {}", + tcx.def_kind(did).descr(did) + ), + ); + } + }; + + if unsatisfied_predicates.is_empty() { + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + + let limit = if candidates.len() == 5 { 5 } else { 4 }; + let type_candidates = candidates + .iter() + .take(limit) + .map(|candidate| { + format!("- `{}`", tcx.at(span).type_of(candidate).subst_identity()) + }) + .collect::>() + .join("\n"); + let additional_types = if candidates.len() > limit { + format!("\nand {} more types", candidates.len() - limit) + } else { + String::new() + }; + + let mut err = struct_span_err!( + tcx.sess, + name.span, + E0220, + "associated type `{name}` not found for `{self_ty}` in the current scope" + ); + err.span_label(name.span, format!("associated item not found in `{self_ty}`")); + err.note(&format!( + "the associated type was found for\n{type_candidates}{additional_types}", + )); + add_def_label(&mut err); + return err.emit(); + } + + let mut bound_spans = Vec::new(); + + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { + let msg = format!( + "doesn't satisfy `{}`", + if obligation.len() > 50 { quiet } else { obligation } + ); + match &self_ty.kind() { + // Point at the type that couldn't satisfy the bound. + ty::Adt(def, _) => bound_spans.push((tcx.def_span(def.did()), msg)), + // Point at the trait object that couldn't satisfy the bound. + ty::Dynamic(preds, _, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => { + bound_spans.push((tcx.def_span(tr.def_id), msg.clone())) + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => {} + } + } + } + // Point at the closure that couldn't satisfy the bound. + ty::Closure(def_id, _) => { + bound_spans.push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`"))) + } + _ => {} + } + }; + + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + let format_pred = |pred: ty::Predicate<'tcx>| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { + let pred = bound_predicate.rebind(pred); + // `::Item = String`. + let projection_ty = pred.skip_binder().projection_ty; + + let substs_with_infer_self = tcx.mk_substs( + std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) + .chain(projection_ty.substs.iter().skip(1)), + ); + + let quiet_projection_ty = + tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self); + + let term = pred.skip_binder().term; + + let obligation = format!("{projection_ty} = {term}"); + let quiet = format!("{quiet_projection_ty} = {term}"); + + bound_span_label(projection_ty.self_ty(), &obligation, &quiet); + Some((obligation, projection_ty.self_ty())) + } + ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { + let p = poly_trait_ref.trait_ref; + let self_ty = p.self_ty(); + let path = p.print_only_trait_path(); + let obligation = format!("{self_ty}: {path}"); + let quiet = format!("_: {path}"); + bound_span_label(self_ty, &obligation, &quiet); + Some((obligation, self_ty)) + } + _ => None, + } + }; + + // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. + // I would do the same here if it didn't mean more code duplication. + let mut bounds: Vec<_> = unsatisfied_predicates + .into_iter() + .filter_map(format_pred) + .map(|(p, _)| format!("`{}`", p)) + .collect(); + bounds.sort(); + bounds.dedup(); + + let mut err = tcx.sess.struct_span_err( + name.span, + &format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") + ); + if !bounds.is_empty() { + err.note(&format!( + "the following trait bounds were not satisfied:\n{}", + bounds.join("\n") + )); + } + err.span_label( + name.span, + format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") + ); + + bound_spans.sort(); + bound_spans.dedup(); + for (span, msg) in bound_spans { + if !tcx.sess.source_map().is_span_accessible(span) { + continue; + } + err.span_label(span, &msg); + } + add_def_label(&mut err); + err.emit() + } + /// When there are any missing associated types, emit an E0191 error and attempt to supply a /// reasonable suggestion on how to write it. For the case of multiple associated types in the /// same trait bound have the same name (as they come from different supertraits), we instead diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 2ff47237b1bf..92bff68cdbce 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -27,7 +27,10 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::DynKind; @@ -40,11 +43,12 @@ use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::astconv_object_safety_violations; use rustc_trait_selection::traits::error_reporting::{ report_object_safety_error, suggestions::NextTypeParamName, }; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; +use rustc_trait_selection::traits::{astconv_object_safety_violations, NormalizeExt}; use smallvec::{smallvec, SmallVec}; use std::collections::BTreeSet; @@ -2043,23 +2047,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - // see if we can satisfy using an inherent associated type - for &impl_ in tcx.inherent_impls(adt_def.did()) { - let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else { - continue; - }; - let ty::Adt(_, adt_substs) = qself_ty.kind() else { - // FIXME(inherent_associated_types) - bug!("unimplemented: non-adt self of inherent assoc ty"); - }; - let item_substs = self.create_substs_for_associated_item( - span, - assoc_ty_did, - assoc_segment, - adt_substs, - ); - let ty = tcx.type_of(assoc_ty_did).subst(tcx, item_substs); - return Ok((ty, DefKind::AssocTy, assoc_ty_did)); + if let Some((ty, did)) = self.lookup_inherent_assoc_ty( + assoc_ident, + assoc_segment, + adt_def.did(), + qself_ty, + hir_ref_id, + span, + )? { + return Ok((ty, DefKind::AssocTy, did)); } } @@ -2202,6 +2198,196 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Ok((ty, DefKind::AssocTy, assoc_ty_did)) } + fn lookup_inherent_assoc_ty( + &self, + name: Ident, + segment: &hir::PathSegment<'_>, + adt_did: DefId, + self_ty: Ty<'tcx>, + block: hir::HirId, + span: Span, + ) -> Result, DefId)>, ErrorGuaranteed> { + let tcx = self.tcx(); + + let candidates: Vec<_> = tcx + .inherent_impls(adt_did) + .iter() + .filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?))) + .collect(); + + if candidates.is_empty() { + return Ok(None); + } + + let cause = ObligationCause::misc(span, block.owner.def_id); + let mut unsatisfied_predicates = Vec::new(); + + for &(impl_, (assoc_item, def_scope)) in &candidates { + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let param_env = tcx.param_env(impl_); + + let impl_ty = tcx.type_of(impl_); + let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_ty = impl_ty.subst(tcx, impl_substs); + + let InferOk { value: impl_ty, obligations } = + infcx.at(&cause, param_env).normalize(impl_ty); + + // Check that the Self-types can be related. + let Ok(InferOk { obligations: sub_obligations, value: () }) = infcx + .at(&ObligationCause::dummy(), param_env) + .define_opaque_types(false) + .sup(impl_ty, self_ty) + else { + continue; + }; + + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_); + let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); + + let InferOk { value: impl_bounds, obligations: norm_obligations } = + infcx.at(&cause, param_env).normalize(impl_bounds); + + let impl_obligations = + traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); + + let candidate_obligations = impl_obligations + .chain(norm_obligations.into_iter()) + .chain(obligations.iter().cloned()); + + let mut matches = true; + + // Evaluate those obligations to see if they might possibly hold. + for o in candidate_obligations { + let o = infcx.resolve_vars_if_possible(o); + if !infcx.predicate_may_hold(&o) { + matches = false; + unsatisfied_predicates.push(o.predicate); + } + } + + // Evaluate those obligations to see if they might possibly hold. + for o in sub_obligations { + let o = infcx.resolve_vars_if_possible(o); + if !infcx.predicate_may_hold(&o) { + matches = false; + unsatisfied_predicates.push(o.predicate); + } + } + + if !matches { + continue; + } + + self.check_assoc_ty(assoc_item, name, def_scope, block, span); + + let ty::Adt(_, adt_substs) = self_ty.kind() else { + bug!("unreachable: `lookup_inherent_assoc_ty` is only called on ADTs"); + }; + + let item_substs = + self.create_substs_for_associated_item(span, assoc_item, segment, adt_substs); + // FIXME(inherent_associated_types): Check if the obligations arising from the + // where-clause & the bounds on the associated type and its parameters hold. + let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); + return Ok(Some((ty, assoc_item))); + } + + Err(self.complain_about_inherent_assoc_type_not_found( + name, + self_ty, + &candidates.into_iter().map(|(impl_, _)| impl_).collect::>(), + unsatisfied_predicates, + span, + )) + } + + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + fn fresh_item_substs(&self, def_id: DefId, infcx: &InferCtxt<'tcx>) -> SubstsRef<'tcx> { + let tcx = self.tcx(); + + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } => infcx + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::SubstitutionPlaceholder, + span: tcx.def_span(def_id), + }) + .into(), + GenericParamDefKind::Const { .. } => { + let span = tcx.def_span(def_id); + let origin = ConstVariableOrigin { + kind: ConstVariableOriginKind::SubstitutionPlaceholder, + span, + }; + infcx + .next_const_var( + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + origin, + ) + .into() + } + }) + } + + fn lookup_assoc_ty( + &self, + name: Ident, + block: hir::HirId, + span: Span, + scope: DefId, + ) -> Option { + let (item, def_scope) = self.lookup_assoc_ty_unchecked(name, block, scope)?; + self.check_assoc_ty(item, name, def_scope, block, span); + Some(item) + } + + fn lookup_assoc_ty_unchecked( + &self, + name: Ident, + block: hir::HirId, + scope: DefId, + ) -> Option<(DefId, DefId)> { + let tcx = self.tcx(); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); + + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead + // of calling `find_by_name_and_kind`. + let item = tcx.associated_items(scope).in_definition_order().find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == ident + })?; + + Some((item.def_id, def_scope)) + } + + fn check_assoc_ty( + &self, + item: DefId, + name: Ident, + def_scope: DefId, + block: hir::HirId, + span: Span, + ) { + let tcx = self.tcx(); + let kind = DefKind::AssocTy; + + if !tcx.visibility(item).is_accessible_from(def_scope, tcx) { + let kind = kind.descr(item); + let msg = format!("{kind} `{name}` is private"); + let def_span = tcx.def_span(item); + tcx.sess + .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624)) + .span_label(span, &format!("private {kind}")) + .span_label(def_span, &format!("{kind} defined here")) + .emit(); + } + tcx.check_stability(item, Some(block), span, None); + } + fn probe_traits_that_match_assoc_ty( &self, qself_ty: Ty<'tcx>, @@ -2255,39 +2441,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .collect() } - fn lookup_assoc_ty( - &self, - ident: Ident, - block: hir::HirId, - span: Span, - scope: DefId, - ) -> Option { - let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); - - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `find_by_name_and_kind`. - let item = tcx.associated_items(scope).in_definition_order().find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == ident - })?; - - let kind = DefKind::AssocTy; - if !item.visibility(tcx).is_accessible_from(def_scope, tcx) { - let kind = kind.descr(item.def_id); - let msg = format!("{kind} `{ident}` is private"); - let def_span = self.tcx().def_span(item.def_id); - tcx.sess - .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624)) - .span_label(span, &format!("private {kind}")) - .span_label(def_span, &format!("{kind} defined here")) - .emit(); - } - tcx.check_stability(item.def_id, Some(block), span, None); - - Some(item.def_id) - } - fn qpath_to_ty( &self, span: Span, diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs new file mode 100644 index 000000000000..191ce23efa6d --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -0,0 +1,35 @@ +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Check that inherent associated types are dispatched on the concrete Self type. + +struct Select(T); + +impl Select { + type Projection = (); +} + +impl Select { + type Projection = bool; +} + +struct Choose(T); +struct NonCopy; + +impl Choose { + type Result = Vec; +} + +impl Choose { + type Result = (); +} + +fn main() { + let _: Select::Projection = false; + let _: Select::Projection = (); + + let _: Choose::Result = (); + let _: Choose<&str>::Result = vec!["..."]; +} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs new file mode 100644 index 000000000000..9b0fa8dc6f32 --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs @@ -0,0 +1,39 @@ +// check-pass + +#![feature(inherent_associated_types, auto_traits, negative_impls)] +#![allow(incomplete_features)] + +use std::cmp::Ordering; + +// Check that inherent associated types are dispatched on the concrete Self type. + +struct Select(T, U); + +impl Select { + type Type = (); +} + +impl Select { + type Type = bool; +} + +impl Select { + type Type = Ordering; +} + +impl Select { + type Type = (bool, bool); +} + +fn main() { + let _: Select::Type = false; + let _: Select::Type = (true, false); + let _: Select::Type = Ordering::Equal; + let _: Select::Type = (); +} + +enum Special {} + +impl !Ordinary for Special {} + +auto trait Ordinary {} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs new file mode 100644 index 000000000000..7b205952f52b --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs @@ -0,0 +1,17 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Parameterized(T, U); + +impl Parameterized<(), ()> { + type Output = bool; +} + +impl Parameterized { + type Result = T; +} + +fn main() { + let _: Parameterized<(), ()>::Output = String::new(); //~ ERROR mismatched types + let _: Parameterized::Result = (); //~ ERROR mismatched types +} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr new file mode 100644 index 000000000000..1c77688b45ac --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/dispatch-on-self-type-2.rs:15:44 + | +LL | let _: Parameterized<(), ()>::Output = String::new(); + | ----------------------------- ^^^^^^^^^^^^^ expected `bool`, found `String` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/dispatch-on-self-type-2.rs:16:47 + | +LL | let _: Parameterized::Result = (); + | -------------------------------- ^^ expected `bool`, found `()` + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr new file mode 100644 index 000000000000..4396435a6ddc --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr @@ -0,0 +1,16 @@ +error[E0220]: associated type `Proj` not found for `Family>` in the current scope + --> $DIR/not-found-self-type-differs.rs:17:34 + | +LL | struct Family(T); + | ---------------- associated item `Proj` not found for this struct +... +LL | type Alias = Family>::Proj; + | ^^^^ associated item not found in `Family>` + | + = note: the associated type was found for + - `Family<()>` + - `Family>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr new file mode 100644 index 000000000000..d527db022172 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr @@ -0,0 +1,16 @@ +error[E0220]: associated type `Proj` not found for `Family` in the current scope + --> $DIR/not-found-self-type-differs.rs:21:40 + | +LL | struct Family(T); + | ---------------- associated item `Proj` not found for this struct +... +LL | let _: Family::Proj = (); + | ^^^^ associated item not found in `Family` + | + = note: the associated type was found for + - `Family<()>` + - `Family>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs new file mode 100644 index 000000000000..93f58dcb6e61 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs @@ -0,0 +1,22 @@ +// revisions: local alias + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Family(T); + +impl Family<()> { + type Proj = (); +} + +impl Family> { + type Proj = Self; +} + +#[cfg(alias)] +type Alias = Family>::Proj; //[alias]~ ERROR associated type `Proj` not found for `Family>` + +fn main() { + #[cfg(local)] + let _: Family::Proj = (); //[local]~ ERROR associated type `Proj` not found for `Family` +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs new file mode 100644 index 000000000000..b00830fa1c15 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs @@ -0,0 +1,21 @@ +// Regression test for issue #104251. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Container(T); + +impl Container { + type Yield = i32; +} + +struct Duple(T, U); + +impl Duple { + type Combination = (T, U); +} + +fn main() { + let _: Container<[u8]>::Yield = 1; //~ ERROR the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied + let _: Duple>::Combination; //~ ERROR the associated type `Combination` exists for `Duple>`, but its trait bounds were not satisfied +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr new file mode 100644 index 000000000000..8b13b6852374 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr @@ -0,0 +1,27 @@ +error: the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds.rs:19:29 + | +LL | struct Container(T); + | --------------------------- associated item `Yield` not found for this struct +... +LL | let _: Container<[u8]>::Yield = 1; + | ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `[u8]: Sized` + +error: the associated type `Combination` exists for `Duple>`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds.rs:20:45 + | +LL | struct Duple(T, U); + | ------------------ associated item `Combination` not found for this struct +... +LL | let _: Duple>::Combination; + | ^^^^^^^^^^^ associated type cannot be referenced on `Duple>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Rc: Send` + `String: Copy` + +error: aborting due to 2 previous errors + From aa7edf70739528746d55915b96c63bd3ec43f3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 18:41:24 +0100 Subject: [PATCH 57/67] Use the correct ParamEnv --- compiler/rustc_hir_analysis/src/astconv/mod.rs | 2 +- .../dispatch-on-self-type-0.rs | 6 ++++++ ...ds.rs => not-found-unsatisfied-bounds-0.rs} | 0 ...r => not-found-unsatisfied-bounds-0.stderr} | 4 ++-- .../not-found-unsatisfied-bounds-1.rs | 18 ++++++++++++++++++ .../not-found-unsatisfied-bounds-1.stderr | 14 ++++++++++++++ 6 files changed, 41 insertions(+), 3 deletions(-) rename tests/ui/associated-inherent-types/{not-found-unsatisfied-bounds.rs => not-found-unsatisfied-bounds-0.rs} (100%) rename tests/ui/associated-inherent-types/{not-found-unsatisfied-bounds.stderr => not-found-unsatisfied-bounds-0.stderr} (91%) create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 92bff68cdbce..f0dd4a5d5ace 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2219,12 +2219,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } + let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); let mut unsatisfied_predicates = Vec::new(); for &(impl_, (assoc_item, def_scope)) in &candidates { let infcx = tcx.infer_ctxt().ignoring_regions().build(); - let param_env = tcx.param_env(impl_); let impl_ty = tcx.type_of(impl_); let impl_substs = self.fresh_item_substs(impl_, &infcx); diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs index 191ce23efa6d..f10386bd9f96 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -33,3 +33,9 @@ fn main() { let _: Choose::Result = (); let _: Choose<&str>::Result = vec!["..."]; } + +// Test if we use the correct `ParamEnv` when proving obligations. + +pub fn parameterized(x: T) { + let _: Choose::Result = vec![x]; +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs similarity index 100% rename from tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs rename to tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr similarity index 91% rename from tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr rename to tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr index 8b13b6852374..736579067615 100644 --- a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr @@ -1,5 +1,5 @@ error: the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied - --> $DIR/not-found-unsatisfied-bounds.rs:19:29 + --> $DIR/not-found-unsatisfied-bounds-0.rs:19:29 | LL | struct Container(T); | --------------------------- associated item `Yield` not found for this struct @@ -11,7 +11,7 @@ LL | let _: Container<[u8]>::Yield = 1; `[u8]: Sized` error: the associated type `Combination` exists for `Duple>`, but its trait bounds were not satisfied - --> $DIR/not-found-unsatisfied-bounds.rs:20:45 + --> $DIR/not-found-unsatisfied-bounds-0.rs:20:45 | LL | struct Duple(T, U); | ------------------ associated item `Combination` not found for this struct diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs new file mode 100644 index 000000000000..c80b1364ae3b --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs @@ -0,0 +1,18 @@ +// fail-check + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Test if we use the correct `ParamEnv` when proving obligations. + +fn parameterized() { + let _: Container::Proj = String::new(); //~ ERROR the associated type `Proj` exists for `Container`, but its trait bounds were not satisfied +} + +struct Container(T); + +impl Container { + type Proj = String; +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr new file mode 100644 index 000000000000..230bfa538b4f --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr @@ -0,0 +1,14 @@ +error: the associated type `Proj` exists for `Container`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds-1.rs:9:26 + | +LL | let _: Container::Proj = String::new(); + | ^^^^ associated type cannot be referenced on `Container` due to unsatisfied trait bounds +... +LL | struct Container(T); + | ------------------- associated item `Proj` not found for this struct + | + = note: the following trait bounds were not satisfied: + `T: Clone` + +error: aborting due to previous error + From cc65ebd0d29a79a2a1d3fd77e679c763837b33c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 18:42:08 +0100 Subject: [PATCH 58/67] Make use of ObligationCtxt --- .../rustc_hir_analysis/src/astconv/errors.rs | 14 ++-- .../rustc_hir_analysis/src/astconv/mod.rs | 73 +++++++------------ 2 files changed, 33 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 191f4f0f910a..0e9f5fb3dae6 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::traits::FulfillmentError; use rustc_middle::ty::{self, Ty}; use rustc_session::parse::feature_err; use rustc_span::lev_distance::find_best_match_for_name; @@ -226,8 +227,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, name: Ident, self_ty: Ty<'tcx>, - candidates: &[DefId], - unsatisfied_predicates: Vec>, + candidates: Vec<(DefId, (DefId, DefId))>, + fulfillment_errors: Vec>, span: Span, ) -> ErrorGuaranteed { let tcx = self.tcx(); @@ -245,16 +246,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - if unsatisfied_predicates.is_empty() { + if fulfillment_errors.is_empty() { // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. let limit = if candidates.len() == 5 { 5 } else { 4 }; let type_candidates = candidates .iter() .take(limit) - .map(|candidate| { - format!("- `{}`", tcx.at(span).type_of(candidate).subst_identity()) - }) + .map(|&(impl_, _)| format!("- `{}`", tcx.at(span).type_of(impl_).subst_identity())) .collect::>() .join("\n"); let additional_types = if candidates.len() > limit { @@ -348,8 +347,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. // I would do the same here if it didn't mean more code duplication. - let mut bounds: Vec<_> = unsatisfied_predicates + let mut bounds: Vec<_> = fulfillment_errors .into_iter() + .map(|error| error.root_obligation.predicate) .filter_map(format_pred) .map(|(p, _)| format!("`{}`", p)) .collect(); diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index f0dd4a5d5ace..d3468f03eedc 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -28,7 +28,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability::AllowUnstable; @@ -42,13 +42,11 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; -use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::{ report_object_safety_error, suggestions::NextTypeParamName, }; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; -use rustc_trait_selection::traits::{astconv_object_safety_violations, NormalizeExt}; +use rustc_trait_selection::traits::{self, astconv_object_safety_violations, ObligationCtxt}; use smallvec::{smallvec, SmallVec}; use std::collections::BTreeSet; @@ -1948,7 +1946,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Res::Err }; - // Check if we have an enum variant. + // Check if we have an enum variant or an inherent associated type. let mut variant_resolution = None; if let Some(adt_def) = self.probe_adt(span, qself_ty) { if adt_def.is_enum() { @@ -2221,62 +2219,37 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); - let mut unsatisfied_predicates = Vec::new(); + let mut fulfillment_errors = Vec::new(); for &(impl_, (assoc_item, def_scope)) in &candidates { let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let ocx = ObligationCtxt::new(&infcx); let impl_ty = tcx.type_of(impl_); let impl_substs = self.fresh_item_substs(impl_, &infcx); let impl_ty = impl_ty.subst(tcx, impl_substs); - - let InferOk { value: impl_ty, obligations } = - infcx.at(&cause, param_env).normalize(impl_ty); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); // Check that the Self-types can be related. - let Ok(InferOk { obligations: sub_obligations, value: () }) = infcx - .at(&ObligationCause::dummy(), param_env) - .define_opaque_types(false) - .sup(impl_ty, self_ty) - else { + // FIXME(fmease): Should we use `eq` here? + if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() { continue; - }; + } // Check whether the impl imposes obligations we have to worry about. let impl_bounds = tcx.predicates_of(impl_); let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - let InferOk { value: impl_bounds, obligations: norm_obligations } = - infcx.at(&cause, param_env).normalize(impl_bounds); + let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); let impl_obligations = traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); - let candidate_obligations = impl_obligations - .chain(norm_obligations.into_iter()) - .chain(obligations.iter().cloned()); + ocx.register_obligations(impl_obligations); - let mut matches = true; - - // Evaluate those obligations to see if they might possibly hold. - for o in candidate_obligations { - let o = infcx.resolve_vars_if_possible(o); - if !infcx.predicate_may_hold(&o) { - matches = false; - unsatisfied_predicates.push(o.predicate); - } - } - - // Evaluate those obligations to see if they might possibly hold. - for o in sub_obligations { - let o = infcx.resolve_vars_if_possible(o); - if !infcx.predicate_may_hold(&o) { - matches = false; - unsatisfied_predicates.push(o.predicate); - } - } - - if !matches { + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors = errors; continue; } @@ -2286,19 +2259,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bug!("unreachable: `lookup_inherent_assoc_ty` is only called on ADTs"); }; - let item_substs = - self.create_substs_for_associated_item(span, assoc_item, segment, adt_substs); - // FIXME(inherent_associated_types): Check if the obligations arising from the - // where-clause & the bounds on the associated type and its parameters hold. + let item_substs = self.create_substs_for_associated_item( + span, assoc_item, segment, + // FIXME(fmease, #107468, #105305): Don't use `adt_substs` here but `impl_substs`. + adt_substs, + ); + + // FIXME(fmease, #106722): Check if the bounds on the parameters of the + // associated type hold, if any. let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); + + // FIXME(fmease): Don't return early here! There might be multiple applicable candidates. return Ok(Some((ty, assoc_item))); } Err(self.complain_about_inherent_assoc_type_not_found( name, self_ty, - &candidates.into_iter().map(|(impl_, _)| impl_).collect::>(), - unsatisfied_predicates, + candidates, + fulfillment_errors, span, )) } From b5e73bfe90f7b4905829f1a6509b61ac5577ee07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 17:06:27 +0100 Subject: [PATCH 59/67] Groundwork for detecting ambiguous candidates NB: Since we are using the same InferCtxt in each iteration, we essentially *spoil* the inference variables and we only ever get at most *one* applicable candidate (only the 1st candidate has clean variables that can still unify correctly). --- .../rustc_hir_analysis/src/astconv/errors.rs | 60 +++++++++++++++++++ .../rustc_hir_analysis/src/astconv/mod.rs | 27 ++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 0e9f5fb3dae6..f3a03805a446 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -222,6 +222,66 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit() } + pub(crate) fn complain_about_ambiguous_inherent_assoc_type( + &self, + name: Ident, + candidates: Vec<(DefId, DefId)>, + span: Span, + ) -> ErrorGuaranteed { + let mut err = struct_span_err!( + self.tcx().sess, + name.span, + E0034, + "multiple applicable items in scope" + ); + err.span_label(name.span, format!("multiple `{name}` found")); + self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span); + err.emit() + } + + // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. + fn note_ambiguous_inherent_assoc_type( + &self, + err: &mut Diagnostic, + candidates: Vec<(DefId, DefId)>, + span: Span, + ) { + let tcx = self.tcx(); + + // Dynamic limit to avoid hiding just one candidate, which is silly. + let limit = if candidates.len() == 5 { 5 } else { 4 }; + + for (index, &(assoc_item, _)) in candidates.iter().take(limit).enumerate() { + let impl_ = tcx.impl_of_method(assoc_item).unwrap(); + + let note_span = if assoc_item.is_local() { + Some(tcx.def_span(assoc_item)) + } else if impl_.is_local() { + Some(tcx.def_span(impl_)) + } else { + None + }; + + let title = if candidates.len() > 1 { + format!("candidate #{}", index + 1) + } else { + "the candidate".into() + }; + + let impl_ty = tcx.at(span).type_of(impl_).subst_identity(); + let note = format!("{title} is defined in an impl for the type `{impl_ty}`"); + + if let Some(span) = note_span { + err.span_note(span, ¬e); + } else { + err.note(¬e); + } + } + if candidates.len() > limit { + err.note(&format!("and {} others", candidates.len() - limit)); + } + } + // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. pub(crate) fn complain_about_inherent_assoc_type_not_found( &self, diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index d3468f03eedc..43db8af7bac8 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2217,12 +2217,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } + // In contexts that have no inference context, just make a new one. + // We do need a local variable to store it, though. + let infcx_; + let infcx = match self.infcx() { + Some(infcx) => infcx, + None => { + assert!(!self_ty.needs_infer()); + infcx_ = tcx.infer_ctxt().ignoring_regions().build(); + &infcx_ + } + }; + let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); let mut fulfillment_errors = Vec::new(); + let mut applicable_candidates = Vec::new(); for &(impl_, (assoc_item, def_scope)) in &candidates { - let infcx = tcx.infer_ctxt().ignoring_regions().build(); let ocx = ObligationCtxt::new(&infcx); let impl_ty = tcx.type_of(impl_); @@ -2253,6 +2265,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { continue; } + applicable_candidates.push((assoc_item, def_scope)); + } + + if applicable_candidates.len() > 1 { + return Err(self.complain_about_ambiguous_inherent_assoc_type( + name, + applicable_candidates, + span, + )); + } + + if let Some((assoc_item, def_scope)) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); let ty::Adt(_, adt_substs) = self_ty.kind() else { @@ -2269,7 +2293,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // associated type hold, if any. let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); - // FIXME(fmease): Don't return early here! There might be multiple applicable candidates. return Ok(Some((ty, assoc_item))); } From 3dc38fbc9115f0e8cf61848938e05e1964ee29bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 16:55:07 +0100 Subject: [PATCH 60/67] Switch from for-loop to filter_map --- .../rustc_hir_analysis/src/astconv/mod.rs | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 43db8af7bac8..c8ff609dea55 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2232,41 +2232,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); let mut fulfillment_errors = Vec::new(); - let mut applicable_candidates = Vec::new(); + let mut applicable_candidates: Vec<_> = candidates + .iter() + .filter_map(|&(impl_, (assoc_item, def_scope))| { + let ocx = ObligationCtxt::new(&infcx); - for &(impl_, (assoc_item, def_scope)) in &candidates { - let ocx = ObligationCtxt::new(&infcx); + let impl_ty = tcx.type_of(impl_); + let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_ty = impl_ty.subst(tcx, impl_substs); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); - let impl_ty = tcx.type_of(impl_); - let impl_substs = self.fresh_item_substs(impl_, &infcx); - let impl_ty = impl_ty.subst(tcx, impl_substs); - let impl_ty = ocx.normalize(&cause, param_env, impl_ty); + // Check that the Self-types can be related. + // FIXME(fmease): Should we use `eq` here? + ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; - // Check that the Self-types can be related. - // FIXME(fmease): Should we use `eq` here? - if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() { - continue; - } + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_); + let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = tcx.predicates_of(impl_); - let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); + let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); - let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); + let impl_obligations = + traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); - let impl_obligations = - traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); + ocx.register_obligations(impl_obligations); - ocx.register_obligations(impl_obligations); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors = errors; + return None; + } - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - fulfillment_errors = errors; - continue; - } - - applicable_candidates.push((assoc_item, def_scope)); - } + Some((assoc_item, def_scope)) + }) + .collect(); if applicable_candidates.len() > 1 { return Err(self.complain_about_ambiguous_inherent_assoc_type( From 6065867a7e4379a12e495912a41318f871104270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 16:58:18 +0100 Subject: [PATCH 61/67] Use InferCtxt::probe to properly detect ambiguous candidates --- .../rustc_hir_analysis/src/astconv/mod.rs | 47 ++++++++++--------- .../ui/associated-inherent-types/ambiguity.rs | 16 +++++++ .../ambiguity.stderr | 20 ++++++++ 3 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 tests/ui/associated-inherent-types/ambiguity.rs create mode 100644 tests/ui/associated-inherent-types/ambiguity.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index c8ff609dea55..c22ebc1c6599 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2235,35 +2235,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut applicable_candidates: Vec<_> = candidates .iter() .filter_map(|&(impl_, (assoc_item, def_scope))| { - let ocx = ObligationCtxt::new(&infcx); + infcx.probe(|_| { + let ocx = ObligationCtxt::new_in_snapshot(&infcx); - let impl_ty = tcx.type_of(impl_); - let impl_substs = self.fresh_item_substs(impl_, &infcx); - let impl_ty = impl_ty.subst(tcx, impl_substs); - let impl_ty = ocx.normalize(&cause, param_env, impl_ty); + let impl_ty = tcx.type_of(impl_); + let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_ty = impl_ty.subst(tcx, impl_substs); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); - // Check that the Self-types can be related. - // FIXME(fmease): Should we use `eq` here? - ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; + // Check that the Self-types can be related. + // FIXME(fmease): Should we use `eq` here? + ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; - // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = tcx.predicates_of(impl_); - let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_); + let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); + let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); - let impl_obligations = - traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); + let impl_obligations = traits::predicates_for_generics( + |_, _| cause.clone(), + param_env, + impl_bounds, + ); - ocx.register_obligations(impl_obligations); + ocx.register_obligations(impl_obligations); - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - fulfillment_errors = errors; - return None; - } + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors = errors; + return None; + } - Some((assoc_item, def_scope)) + Some((assoc_item, def_scope)) + }) }) .collect(); diff --git a/tests/ui/associated-inherent-types/ambiguity.rs b/tests/ui/associated-inherent-types/ambiguity.rs new file mode 100644 index 000000000000..73920555b3e0 --- /dev/null +++ b/tests/ui/associated-inherent-types/ambiguity.rs @@ -0,0 +1,16 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Wrapper(T); + +impl Wrapper { + type Foo = i32; +} + +impl Wrapper<()> { + type Foo = (); +} + +fn main() { + let _: Wrapper<_>::Foo = (); //~ ERROR multiple applicable items in scope +} diff --git a/tests/ui/associated-inherent-types/ambiguity.stderr b/tests/ui/associated-inherent-types/ambiguity.stderr new file mode 100644 index 000000000000..155c296cbb3c --- /dev/null +++ b/tests/ui/associated-inherent-types/ambiguity.stderr @@ -0,0 +1,20 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/ambiguity.rs:15:24 + | +LL | let _: Wrapper<_>::Foo = (); + | ^^^ multiple `Foo` found + | +note: candidate #1 is defined in an impl for the type `Wrapper` + --> $DIR/ambiguity.rs:7:5 + | +LL | type Foo = i32; + | ^^^^^^^^ +note: candidate #2 is defined in an impl for the type `Wrapper<()>` + --> $DIR/ambiguity.rs:11:5 + | +LL | type Foo = (); + | ^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`. From 77ea90ec71926df7d478834d34d1fefce40cc456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 18:42:47 +0100 Subject: [PATCH 62/67] Fix substitution bug --- .../rustc_hir_analysis/src/astconv/errors.rs | 12 +++++----- .../rustc_hir_analysis/src/astconv/mod.rs | 19 +++++++-------- .../dispatch-on-self-type-0.rs | 2 +- .../dispatch-on-self-type-2.stderr | 2 +- .../substitute-params-bad.rs | 23 +++++++++++++++++++ .../substitute-params-bad.stderr | 20 ++++++++++++++++ ...truct-generics.rs => substitute-params.rs} | 8 +++++++ 7 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 tests/ui/associated-inherent-types/substitute-params-bad.rs create mode 100644 tests/ui/associated-inherent-types/substitute-params-bad.stderr rename tests/ui/associated-inherent-types/{struct-generics.rs => substitute-params.rs} (55%) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index f3a03805a446..b039e654fd00 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -225,7 +225,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub(crate) fn complain_about_ambiguous_inherent_assoc_type( &self, name: Ident, - candidates: Vec<(DefId, DefId)>, + candidates: Vec, span: Span, ) -> ErrorGuaranteed { let mut err = struct_span_err!( @@ -243,7 +243,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn note_ambiguous_inherent_assoc_type( &self, err: &mut Diagnostic, - candidates: Vec<(DefId, DefId)>, + candidates: Vec, span: Span, ) { let tcx = self.tcx(); @@ -251,11 +251,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Dynamic limit to avoid hiding just one candidate, which is silly. let limit = if candidates.len() == 5 { 5 } else { 4 }; - for (index, &(assoc_item, _)) in candidates.iter().take(limit).enumerate() { - let impl_ = tcx.impl_of_method(assoc_item).unwrap(); + for (index, &item) in candidates.iter().take(limit).enumerate() { + let impl_ = tcx.impl_of_method(item).unwrap(); - let note_span = if assoc_item.is_local() { - Some(tcx.def_span(assoc_item)) + let note_span = if item.is_local() { + Some(tcx.def_span(item)) } else if impl_.is_local() { Some(tcx.def_span(impl_)) } else { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index c22ebc1c6599..f43b92254eb0 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2267,7 +2267,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return None; } - Some((assoc_item, def_scope)) + // FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot. + Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs))) }) }) .collect(); @@ -2275,23 +2276,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if applicable_candidates.len() > 1 { return Err(self.complain_about_ambiguous_inherent_assoc_type( name, - applicable_candidates, + applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(), span, )); } - if let Some((assoc_item, def_scope)) = applicable_candidates.pop() { + if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); - let ty::Adt(_, adt_substs) = self_ty.kind() else { - bug!("unreachable: `lookup_inherent_assoc_ty` is only called on ADTs"); - }; + // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, + // we still need to register region obligations for regionck to prove/disprove. - let item_substs = self.create_substs_for_associated_item( - span, assoc_item, segment, - // FIXME(fmease, #107468, #105305): Don't use `adt_substs` here but `impl_substs`. - adt_substs, - ); + let item_substs = + self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs); // FIXME(fmease, #106722): Check if the bounds on the parameters of the // associated type hold, if any. diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs index f10386bd9f96..f846bfa4168a 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -31,7 +31,7 @@ fn main() { let _: Select::Projection = (); let _: Choose::Result = (); - let _: Choose<&str>::Result = vec!["..."]; + let _: Choose::Result = vec![true]; } // Test if we use the correct `ParamEnv` when proving obligations. diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr index 1c77688b45ac..c9a48872af40 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr @@ -10,7 +10,7 @@ error[E0308]: mismatched types --> $DIR/dispatch-on-self-type-2.rs:16:47 | LL | let _: Parameterized::Result = (); - | -------------------------------- ^^ expected `bool`, found `()` + | -------------------------------- ^^ expected `u32`, found `()` | | | expected due to this diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.rs b/tests/ui/associated-inherent-types/substitute-params-bad.rs new file mode 100644 index 000000000000..00eb1a14da47 --- /dev/null +++ b/tests/ui/associated-inherent-types/substitute-params-bad.rs @@ -0,0 +1,23 @@ +// Regression test for issue #105305 and for +// https://github.com/rust-lang/rust/issues/107468#issuecomment-1409096700 + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { //~ ERROR lifetime parameters must be declared prior to type and const parameters + type P = T; +} + +struct Subj(T); + +impl Subj<(T, S)> { + type Un = (T, S); +} + +fn main() { + type A = S<()>::P; + + let _: Subj<(i32, i32)>::Un = 0i32; //~ ERROR mismatched types +} diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.stderr b/tests/ui/associated-inherent-types/substitute-params-bad.stderr new file mode 100644 index 000000000000..7a7808ba67b1 --- /dev/null +++ b/tests/ui/associated-inherent-types/substitute-params-bad.stderr @@ -0,0 +1,20 @@ +error: lifetime parameters must be declared prior to type and const parameters + --> $DIR/substitute-params-bad.rs:9:9 + | +LL | impl S { + | ----^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T>` + +error[E0308]: mismatched types + --> $DIR/substitute-params-bad.rs:22:35 + | +LL | let _: Subj<(i32, i32)>::Un = 0i32; + | -------------------- ^^^^ expected `(i32, i32)`, found `i32` + | | + | expected due to this + | + = note: expected tuple `(i32, i32)` + found type `i32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/struct-generics.rs b/tests/ui/associated-inherent-types/substitute-params.rs similarity index 55% rename from tests/ui/associated-inherent-types/struct-generics.rs rename to tests/ui/associated-inherent-types/substitute-params.rs index 8952b3791730..e94d68331590 100644 --- a/tests/ui/associated-inherent-types/struct-generics.rs +++ b/tests/ui/associated-inherent-types/substitute-params.rs @@ -9,7 +9,15 @@ impl S { type P = T; } +impl S<(T,)> { + type Un = T; +} + fn main() { + // Regression test for issue #104240. type A = S<()>::P; let _: A = (); + + // Regression test for issue #107468. + let _: S<(i32,)>::Un = 0i32; } From 6eb6455c46454fc09fe892d3f9193477a323802c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 1 Feb 2023 17:20:55 +0100 Subject: [PATCH 63/67] Add a test and several known bugs --- .../bugs/ice-substitution.rs | 23 ++++++++++++++ .../bugs/ice-substitution.stderr | 6 ++++ .../bugs/inference-fail.rs | 15 +++++++++ .../bugs/inference-fail.stderr | 9 ++++++ .../bugs/lack-of-regionck.rs | 19 ++++++++++++ ...-self-type-differs-shadowing-trait-item.rs | 31 +++++++++++++++++++ ...ffers-shadowing-trait-item.shadowed.stderr | 15 +++++++++ ...fers-shadowing-trait-item.uncovered.stderr | 9 ++++++ 8 files changed, 127 insertions(+) create mode 100644 tests/ui/associated-inherent-types/bugs/ice-substitution.rs create mode 100644 tests/ui/associated-inherent-types/bugs/ice-substitution.stderr create mode 100644 tests/ui/associated-inherent-types/bugs/inference-fail.rs create mode 100644 tests/ui/associated-inherent-types/bugs/inference-fail.stderr create mode 100644 tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs new file mode 100644 index 000000000000..53ac79e0561b --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs @@ -0,0 +1,23 @@ +// known-bug: unknown +// failure-status: 101 +// normalize-stderr-test "note: .*\n\n" -> "" +// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" +// rustc-env:RUST_BACKTRACE=0 + +// FIXME: I presume a type variable that couldn't be solved by `resolve_vars_if_possible` +// escapes the InferCtxt snapshot. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Cont(T); + +impl Cont { + type Out = Vec; +} + +pub fn weird(x: T) { + let _: Cont<_>::Out = vec![true]; +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr new file mode 100644 index 000000000000..7b0d1c505162 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr @@ -0,0 +1,6 @@ +error: the compiler unexpectedly panicked. this is a bug. + +query stack during panic: +#0 [typeck] type-checking `weird` +#1 [typeck_item_bodies] type-checking all item bodies +end of query stack diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.rs b/tests/ui/associated-inherent-types/bugs/inference-fail.rs new file mode 100644 index 000000000000..a920b412b1a4 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/inference-fail.rs @@ -0,0 +1,15 @@ +// known-bug: unknown + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S<()> { + type P = i128; +} + +fn main() { + // We fail to infer `_ == ()` here. + let _: S<_>::P; +} diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr b/tests/ui/associated-inherent-types/bugs/inference-fail.stderr new file mode 100644 index 000000000000..425691bd6c4f --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/inference-fail.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/inference-fail.rs:14:14 + | +LL | let _: S<_>::P; + | ^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs new file mode 100644 index 000000000000..632dbf3854b2 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs @@ -0,0 +1,19 @@ +// known-bug: unknown +// check-pass + +// We currently don't region-check inherent associated type projections at all. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features, dead_code)] + +struct S(T); + +impl S<&'static ()> { + type T = (); +} + +fn usr<'a>() { + let _: S::<&'a ()>::T; // this should *fail* but it doesn't! +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs new file mode 100644 index 000000000000..d2efb24c6662 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs @@ -0,0 +1,31 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Check that it's okay to report “[inherent] associated type […] not found” for inherent associated +// type candidates that are not applicable (due to unsuitable Self type) even if there exists a +// “shadowed” associated type from a trait with the same name since its use would be ambiguous +// anyway if the IAT didn't exist. +// FIXME(inherent_associated_types): Figure out which error would be more helpful here. + +// revisions: shadowed uncovered + +struct S(T); + +trait Tr { + type Pr; +} + +impl Tr for S { + type Pr = (); +} + +#[cfg(shadowed)] +impl S<()> { + type Pr = i32; +} + +fn main() { + let _: S::::Pr = (); + //[shadowed]~^ ERROR associated type `Pr` not found + //[uncovered]~^^ ERROR ambiguous associated type +} diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr new file mode 100644 index 000000000000..3561db354c03 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr @@ -0,0 +1,15 @@ +error[E0220]: associated type `Pr` not found for `S` in the current scope + --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23 + | +LL | struct S(T); + | ----------- associated item `Pr` not found for this struct +... +LL | let _: S::::Pr = (); + | ^^ associated item not found in `S` + | + = note: the associated type was found for + - `S<()>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr new file mode 100644 index 000000000000..88c72042ce2d --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr @@ -0,0 +1,9 @@ +error[E0223]: ambiguous associated type + --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:12 + | +LL | let _: S::::Pr = (); + | ^^^^^^^^^^^^^ help: use the fully-qualified path: ` as Tr>::Pr` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0223`. From 569ca2bad04dec9c6b7bc4864bd0216e3bea9c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Feb 2023 17:21:29 +0100 Subject: [PATCH 64/67] Deduplicate fresh_item_substs --- .../rustc_hir_analysis/src/astconv/mod.rs | 65 ++++++++++--------- compiler/rustc_hir_typeck/src/method/probe.rs | 30 +-------- 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index f43b92254eb0..4098de59f9e9 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2239,7 +2239,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let ocx = ObligationCtxt::new_in_snapshot(&infcx); let impl_ty = tcx.type_of(impl_); - let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_substs = infcx.fresh_item_substs(impl_); let impl_ty = impl_ty.subst(tcx, impl_substs); let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -2306,36 +2306,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )) } - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. - fn fresh_item_substs(&self, def_id: DefId, infcx: &InferCtxt<'tcx>) -> SubstsRef<'tcx> { - let tcx = self.tcx(); - - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => infcx - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - infcx - .next_const_var( - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - origin, - ) - .into() - } - }) - } - fn lookup_assoc_ty( &self, name: Ident, @@ -3531,3 +3501,36 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } + +pub trait InferCtxtExt<'tcx> { + fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } => self + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::SubstitutionPlaceholder, + span: self.tcx.def_span(def_id), + }) + .into(), + GenericParamDefKind::Const { .. } => { + let span = self.tcx.def_span(def_id); + let origin = ConstVariableOrigin { + kind: ConstVariableOriginKind::SubstitutionPlaceholder, + span, + }; + self.next_const_var( + self.tcx + .type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + origin, + ) + .into() + } + }) + } +} diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 0b9226802cf5..eb6c0b7686b6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,12 +9,11 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; @@ -1941,33 +1940,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) } - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var( - self.tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - origin, - ) - .into() - } - }) - } - /// Replaces late-bound-regions bound by `value` with `'static` using /// `ty::erase_late_bound_regions`. /// From 00b976a1381b301a74b4f97f041de9a6cc58dba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Feb 2023 18:28:44 +0100 Subject: [PATCH 65/67] Collect fulfillment errors across impls --- .../rustc_hir_analysis/src/astconv/mod.rs | 4 ++-- ...nd-unsatisfied-bounds-in-multiple-impls.rs | 20 +++++++++++++++++++ ...nsatisfied-bounds-in-multiple-impls.stderr | 20 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 4098de59f9e9..03b1af76f33b 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2261,9 +2261,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ocx.register_obligations(impl_obligations); - let errors = ocx.select_where_possible(); + let mut errors = ocx.select_where_possible(); if !errors.is_empty() { - fulfillment_errors = errors; + fulfillment_errors.append(&mut errors); return None; } diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs new file mode 100644 index 000000000000..5b0e8de9c580 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs @@ -0,0 +1,20 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(A, B); +struct Featureless; + +trait One {} +trait Two {} + +impl S { + type X = (); +} + +impl S { + type X = String; +} + +fn main() { + let _: S::::X; //~ ERROR the associated type `X` exists for `S`, but its trait bounds were not satisfied +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr new file mode 100644 index 000000000000..3ddab25deb54 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr @@ -0,0 +1,20 @@ +error: the associated type `X` exists for `S`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43 + | +LL | struct S(A, B); + | -------------- associated item `X` not found for this struct +LL | struct Featureless; + | ------------------ + | | + | doesn't satisfy `Featureless: One` + | doesn't satisfy `Featureless: Two` +... +LL | let _: S::::X; + | ^ associated type cannot be referenced on `S` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Featureless: One` + `Featureless: Two` + +error: aborting due to previous error + From f2253dad24ad541618c939f363688d4e90fca72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Feb 2023 22:54:47 +0100 Subject: [PATCH 66/67] Add some FIXMEs for follow-up PRs --- compiler/rustc_hir_analysis/src/astconv/errors.rs | 8 ++++++-- compiler/rustc_hir_analysis/src/astconv/mod.rs | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index b039e654fd00..a9c2886b4144 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -291,6 +291,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fulfillment_errors: Vec>, span: Span, ) -> ErrorGuaranteed { + // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. + // Either + // * update this code by applying changes similar to #106702 or by taking a + // Vec<(DefId, (DefId, DefId), Option>>)> or + // * deduplicate this code across the two crates. + let tcx = self.tcx(); let adt_did = self_ty.ty_adt_def().map(|def| def.did()); @@ -338,7 +344,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut bound_spans = Vec::new(); - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { let msg = format!( "doesn't satisfy `{}`", @@ -367,7 +372,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. let format_pred = |pred: ty::Predicate<'tcx>| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 03b1af76f33b..716b4fc6ae36 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2284,8 +2284,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); - // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, - // we still need to register region obligations for regionck to prove/disprove. + // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still + // need to relate the Self-type with fresh item substs & register region obligations for + // regionck to prove/disprove. let item_substs = self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs); From 2f163554866e1b4b348811e9df6a4a753beb0abf Mon Sep 17 00:00:00 2001 From: Jesus Checa Hidalgo Date: Wed, 22 Feb 2023 08:25:14 +0100 Subject: [PATCH 67/67] Add no-fail-fast support to miri, rustfmt and clippy testsuites This commit adds `--no-fail-fast` flag to each `cargo test` command in each tool Step trait implementation. Fixes #108261 --- src/bootstrap/test.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 9cd6107b43ac..b4f1506dc8f3 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -435,6 +435,10 @@ impl Step for Rustfmt { &[], ); + if !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + let dir = testdir(builder, compiler.host); t!(fs::create_dir_all(&dir)); cargo.env("RUSTFMT_TEST_DIR", dir); @@ -615,6 +619,10 @@ impl Step for Miri { ); cargo.add_rustc_lib_path(builder, compiler); + if !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); @@ -746,6 +754,10 @@ impl Step for Clippy { &[], ); + if !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir());