mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-16 05:56:56 +00:00
Auto merge of #110821 - matthiaskrgr:rollup-mzxrvw7, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #110556 (Switch to `EarlyBinder` for `explicit_item_bounds`) - #110615 (Add `impl_tag!` macro to implement `Tag` for tagged pointer easily) - #110649 (Fix no_global_oom_handling build) - #110671 (Consider polarity in new solver) - #110783 (Fix ICE on --print=... i/o errors) - #110796 (Updating Wake example to use new 'pin!' macro) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
458d4dae84
@ -702,7 +702,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
.copied()
|
||||
.find_map(find_fn_kind_from_did),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
|
||||
.bound_explicit_item_bounds(def_id)
|
||||
.explicit_item_bounds(def_id)
|
||||
.subst_iter_copied(tcx, substs)
|
||||
.find_map(find_fn_kind_from_did),
|
||||
ty::Closure(_, substs) => match substs.as_closure().kind() {
|
||||
|
@ -31,6 +31,7 @@
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(ptr_alignment_type)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
|
@ -24,6 +24,7 @@ use crate::aligned::Aligned;
|
||||
|
||||
mod copy;
|
||||
mod drop;
|
||||
mod impl_tag;
|
||||
|
||||
pub use copy::CopyTaggedPtr;
|
||||
pub use drop::TaggedPtr;
|
||||
@ -141,6 +142,30 @@ pub unsafe trait Tag: Copy {
|
||||
unsafe fn from_usize(tag: usize) -> Self;
|
||||
}
|
||||
|
||||
/// Returns the number of bits available for use for tags in a pointer to `T`
|
||||
/// (this is based on `T`'s alignment).
|
||||
pub const fn bits_for<T: ?Sized + Aligned>() -> u32 {
|
||||
crate::aligned::align_of::<T>().as_nonzero().trailing_zeros()
|
||||
}
|
||||
|
||||
/// Returns the correct [`Tag::BITS`] constant for a set of tag values.
|
||||
pub const fn bits_for_tags(mut tags: &[usize]) -> u32 {
|
||||
let mut bits = 0;
|
||||
|
||||
while let &[tag, ref rest @ ..] = tags {
|
||||
tags = rest;
|
||||
|
||||
// bits required to represent `tag`,
|
||||
// position of the most significant 1
|
||||
let b = usize::BITS - tag.leading_zeros();
|
||||
if b > bits {
|
||||
bits = b;
|
||||
}
|
||||
}
|
||||
|
||||
bits
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Aligned> Pointer for Box<T> {
|
||||
const BITS: u32 = bits_for::<Self::Target>();
|
||||
|
||||
@ -221,12 +246,6 @@ unsafe impl<'a, T: 'a + ?Sized + Aligned> Pointer for &'a mut T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bits available for use for tags in a pointer to `T`
|
||||
/// (this is based on `T`'s alignment).
|
||||
pub const fn bits_for<T: ?Sized + Aligned>() -> u32 {
|
||||
crate::aligned::align_of::<T>().as_nonzero().trailing_zeros()
|
||||
}
|
||||
|
||||
/// A tag type used in [`CopyTaggedPtr`] and [`TaggedPtr`] tests.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg(test)]
|
||||
|
144
compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs
Normal file
144
compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs
Normal file
@ -0,0 +1,144 @@
|
||||
/// Implements [`Tag`] for a given type.
|
||||
///
|
||||
/// You can use `impl_tag` on structs and enums.
|
||||
/// You need to specify the type and all its possible values,
|
||||
/// which can only be paths with optional fields.
|
||||
///
|
||||
/// [`Tag`]: crate::tagged_ptr::Tag
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(macro_metavar_expr)]
|
||||
/// use rustc_data_structures::{impl_tag, tagged_ptr::Tag};
|
||||
///
|
||||
/// #[derive(Copy, Clone, PartialEq, Debug)]
|
||||
/// enum SomeTag {
|
||||
/// A,
|
||||
/// B,
|
||||
/// X { v: bool },
|
||||
/// Y(bool, bool),
|
||||
/// }
|
||||
///
|
||||
/// impl_tag! {
|
||||
/// // The type for which the `Tag` will be implemented
|
||||
/// impl Tag for SomeTag;
|
||||
/// // You need to specify all possible tag values:
|
||||
/// SomeTag::A, // 0
|
||||
/// SomeTag::B, // 1
|
||||
/// // For variants with fields, you need to specify the fields:
|
||||
/// SomeTag::X { v: true }, // 2
|
||||
/// SomeTag::X { v: false }, // 3
|
||||
/// // For tuple variants use named syntax:
|
||||
/// SomeTag::Y { 0: true, 1: true }, // 4
|
||||
/// SomeTag::Y { 0: false, 1: true }, // 5
|
||||
/// SomeTag::Y { 0: true, 1: false }, // 6
|
||||
/// SomeTag::Y { 0: false, 1: false }, // 7
|
||||
/// }
|
||||
///
|
||||
/// // Tag values are assigned in order:
|
||||
/// assert_eq!(SomeTag::A.into_usize(), 0);
|
||||
/// assert_eq!(SomeTag::X { v: false }.into_usize(), 3);
|
||||
/// assert_eq!(SomeTag::Y(false, true).into_usize(), 5);
|
||||
///
|
||||
/// assert_eq!(unsafe { SomeTag::from_usize(1) }, SomeTag::B);
|
||||
/// assert_eq!(unsafe { SomeTag::from_usize(2) }, SomeTag::X { v: true });
|
||||
/// assert_eq!(unsafe { SomeTag::from_usize(7) }, SomeTag::Y(false, false));
|
||||
/// ```
|
||||
///
|
||||
/// Structs are supported:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(macro_metavar_expr)]
|
||||
/// # use rustc_data_structures::impl_tag;
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// struct Flags { a: bool, b: bool }
|
||||
///
|
||||
/// impl_tag! {
|
||||
/// impl Tag for Flags;
|
||||
/// Flags { a: true, b: true },
|
||||
/// Flags { a: false, b: true },
|
||||
/// Flags { a: true, b: false },
|
||||
/// Flags { a: false, b: false },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Not specifying all values results in a compile error:
|
||||
///
|
||||
/// ```compile_fail,E0004
|
||||
/// #![feature(macro_metavar_expr)]
|
||||
/// # use rustc_data_structures::impl_tag;
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// enum E {
|
||||
/// A,
|
||||
/// B,
|
||||
/// }
|
||||
///
|
||||
/// impl_tag! {
|
||||
/// impl Tag for E;
|
||||
/// E::A,
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_tag {
|
||||
(
|
||||
impl Tag for $Self:ty;
|
||||
$(
|
||||
$($path:ident)::* $( { $( $fields:tt )* })?,
|
||||
)*
|
||||
) => {
|
||||
// Safety:
|
||||
// `bits_for_tags` is called on the same `${index()}`-es as
|
||||
// `into_usize` returns, thus `BITS` constant is correct.
|
||||
unsafe impl $crate::tagged_ptr::Tag for $Self {
|
||||
const BITS: u32 = $crate::tagged_ptr::bits_for_tags(&[
|
||||
$(
|
||||
${index()},
|
||||
$( ${ignore(path)} )*
|
||||
)*
|
||||
]);
|
||||
|
||||
#[inline]
|
||||
fn into_usize(self) -> usize {
|
||||
// This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc)
|
||||
// (or at least it should, see <https://github.com/rust-lang/rust/issues/110613>)
|
||||
#[forbid(unreachable_patterns)]
|
||||
match self {
|
||||
// `match` is doing heavy lifting here, by requiring exhaustiveness
|
||||
$(
|
||||
$($path)::* $( { $( $fields )* } )? => ${index()},
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_usize(tag: usize) -> Self {
|
||||
match tag {
|
||||
$(
|
||||
${index()} => $($path)::* $( { $( $fields )* } )?,
|
||||
)*
|
||||
|
||||
// Safety:
|
||||
// `into_usize` only returns `${index()}` of the same
|
||||
// repetition as we are filtering above, thus if this is
|
||||
// reached, the safety contract of this function was
|
||||
// already breached.
|
||||
_ => unsafe {
|
||||
debug_assert!(
|
||||
false,
|
||||
"invalid tag: {tag}\
|
||||
(this is a bug in the caller of `from_usize`)"
|
||||
);
|
||||
std::hint::unreachable_unchecked()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
@ -0,0 +1,34 @@
|
||||
#[test]
|
||||
fn bits_constant() {
|
||||
use crate::tagged_ptr::Tag;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Unit;
|
||||
impl_tag! { impl Tag for Unit; Unit, }
|
||||
assert_eq!(Unit::BITS, 0);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Enum3 {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
impl_tag! { impl Tag for Enum3; Enum3::A, Enum3::B, Enum3::C, }
|
||||
assert_eq!(Enum3::BITS, 2);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Eight(bool, bool, bool);
|
||||
impl_tag! {
|
||||
impl Tag for Eight;
|
||||
Eight { 0: true, 1: true, 2: true },
|
||||
Eight { 0: true, 1: true, 2: false },
|
||||
Eight { 0: true, 1: false, 2: true },
|
||||
Eight { 0: true, 1: false, 2: false },
|
||||
Eight { 0: false, 1: true, 2: true },
|
||||
Eight { 0: false, 1: true, 2: false },
|
||||
Eight { 0: false, 1: false, 2: true },
|
||||
Eight { 0: false, 1: false, 2: false },
|
||||
}
|
||||
|
||||
assert_eq!(Eight::BITS, 3);
|
||||
}
|
@ -58,8 +58,16 @@ use std::str;
|
||||
use std::sync::LazyLock;
|
||||
use std::time::Instant;
|
||||
|
||||
// This import blocks the use of panicking `print` and `println` in all the code
|
||||
// below. Please use `safe_print` and `safe_println` to avoid ICE when
|
||||
// encountering an I/O error during print.
|
||||
#[allow(unused_imports)]
|
||||
use std::{compile_error as print, compile_error as println};
|
||||
|
||||
pub mod args;
|
||||
pub mod pretty;
|
||||
#[macro_use]
|
||||
mod print;
|
||||
mod session_diagnostics;
|
||||
|
||||
use crate::session_diagnostics::{
|
||||
@ -511,7 +519,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
|
||||
if io::stdout().is_terminal() {
|
||||
show_content_with_pager(&text);
|
||||
} else {
|
||||
print!("{text}");
|
||||
safe_print!("{text}");
|
||||
}
|
||||
}
|
||||
Err(InvalidErrorCode) => {
|
||||
@ -547,7 +555,7 @@ fn show_content_with_pager(content: &str) {
|
||||
// If pager fails for whatever reason, we should still print the content
|
||||
// to standard output
|
||||
if fallback_to_println {
|
||||
print!("{content}");
|
||||
safe_print!("{content}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -601,7 +609,7 @@ pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Co
|
||||
let path = &(*ifile);
|
||||
let mut v = Vec::new();
|
||||
locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
|
||||
println!("{}", String::from_utf8(v).unwrap());
|
||||
safe_println!("{}", String::from_utf8(v).unwrap());
|
||||
}
|
||||
Input::Str { .. } => {
|
||||
early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
|
||||
@ -642,12 +650,12 @@ fn print_crate_info(
|
||||
TargetList => {
|
||||
let mut targets = rustc_target::spec::TARGETS.to_vec();
|
||||
targets.sort_unstable();
|
||||
println!("{}", targets.join("\n"));
|
||||
safe_println!("{}", targets.join("\n"));
|
||||
}
|
||||
Sysroot => println!("{}", sess.sysroot.display()),
|
||||
TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
|
||||
Sysroot => safe_println!("{}", sess.sysroot.display()),
|
||||
TargetLibdir => safe_println!("{}", sess.target_tlib_path.dir.display()),
|
||||
TargetSpec => {
|
||||
println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
|
||||
safe_println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
|
||||
}
|
||||
AllTargetSpecs => {
|
||||
let mut targets = BTreeMap::new();
|
||||
@ -656,7 +664,7 @@ fn print_crate_info(
|
||||
let target = Target::expect_builtin(&triple);
|
||||
targets.insert(name, target.to_json());
|
||||
}
|
||||
println!("{}", serde_json::to_string_pretty(&targets).unwrap());
|
||||
safe_println!("{}", serde_json::to_string_pretty(&targets).unwrap());
|
||||
}
|
||||
FileNames | CrateName => {
|
||||
let Some(attrs) = attrs.as_ref() else {
|
||||
@ -666,14 +674,14 @@ fn print_crate_info(
|
||||
let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
|
||||
let id = rustc_session::output::find_crate_name(sess, attrs);
|
||||
if *req == PrintRequest::CrateName {
|
||||
println!("{id}");
|
||||
safe_println!("{id}");
|
||||
continue;
|
||||
}
|
||||
let crate_types = collect_crate_types(sess, attrs);
|
||||
for &style in &crate_types {
|
||||
let fname =
|
||||
rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
|
||||
println!("{}", fname.file_name().unwrap().to_string_lossy());
|
||||
safe_println!("{}", fname.file_name().unwrap().to_string_lossy());
|
||||
}
|
||||
}
|
||||
Cfg => {
|
||||
@ -707,13 +715,13 @@ fn print_crate_info(
|
||||
|
||||
cfgs.sort();
|
||||
for cfg in cfgs {
|
||||
println!("{cfg}");
|
||||
safe_println!("{cfg}");
|
||||
}
|
||||
}
|
||||
CallingConventions => {
|
||||
let mut calling_conventions = rustc_target::spec::abi::all_names();
|
||||
calling_conventions.sort_unstable();
|
||||
println!("{}", calling_conventions.join("\n"));
|
||||
safe_println!("{}", calling_conventions.join("\n"));
|
||||
}
|
||||
RelocationModels
|
||||
| CodeModels
|
||||
@ -733,7 +741,7 @@ fn print_crate_info(
|
||||
let stable = sess.target.options.supported_split_debuginfo.contains(split);
|
||||
let unstable_ok = sess.unstable_options();
|
||||
if stable || unstable_ok {
|
||||
println!("{split}");
|
||||
safe_println!("{split}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -770,14 +778,14 @@ pub fn version_at_macro_invocation(
|
||||
) {
|
||||
let verbose = matches.opt_present("verbose");
|
||||
|
||||
println!("{binary} {version}");
|
||||
safe_println!("{binary} {version}");
|
||||
|
||||
if verbose {
|
||||
println!("binary: {binary}");
|
||||
println!("commit-hash: {commit_hash}");
|
||||
println!("commit-date: {commit_date}");
|
||||
println!("host: {}", config::host_triple());
|
||||
println!("release: {release}");
|
||||
safe_println!("binary: {binary}");
|
||||
safe_println!("commit-hash: {commit_hash}");
|
||||
safe_println!("commit-date: {commit_date}");
|
||||
safe_println!("host: {}", config::host_triple());
|
||||
safe_println!("release: {release}");
|
||||
|
||||
let debug_flags = matches.opt_strs("Z");
|
||||
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
|
||||
@ -807,7 +815,7 @@ fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
|
||||
} else {
|
||||
""
|
||||
};
|
||||
println!(
|
||||
safe_println!(
|
||||
"{options}{at_path}\nAdditional help:
|
||||
-C help Print codegen options
|
||||
-W help \
|
||||
@ -820,7 +828,7 @@ fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
|
||||
}
|
||||
|
||||
fn print_wall_help() {
|
||||
println!(
|
||||
safe_println!(
|
||||
"
|
||||
The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by
|
||||
default. Use `rustc -W help` to see all available lints. It's more common to put
|
||||
@ -832,7 +840,7 @@ the command line flag directly.
|
||||
|
||||
/// Write to stdout lint command options, together with a list of all available lints
|
||||
pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
|
||||
println!(
|
||||
safe_println!(
|
||||
"
|
||||
Available lint options:
|
||||
-W <foo> Warn about <foo>
|
||||
@ -877,21 +885,21 @@ Available lint options:
|
||||
s
|
||||
};
|
||||
|
||||
println!("Lint checks provided by rustc:\n");
|
||||
safe_println!("Lint checks provided by rustc:\n");
|
||||
|
||||
let print_lints = |lints: Vec<&Lint>| {
|
||||
println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
|
||||
println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
|
||||
safe_println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
|
||||
safe_println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
|
||||
for lint in lints {
|
||||
let name = lint.name_lower().replace('_', "-");
|
||||
println!(
|
||||
safe_println!(
|
||||
" {} {:7.7} {}",
|
||||
padded(&name),
|
||||
lint.default_level(sess.edition()).as_str(),
|
||||
lint.desc
|
||||
);
|
||||
}
|
||||
println!("\n");
|
||||
safe_println!("\n");
|
||||
};
|
||||
|
||||
print_lints(builtin);
|
||||
@ -912,14 +920,14 @@ Available lint options:
|
||||
s
|
||||
};
|
||||
|
||||
println!("Lint groups provided by rustc:\n");
|
||||
safe_println!("Lint groups provided by rustc:\n");
|
||||
|
||||
let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>, all_warnings| {
|
||||
println!(" {} sub-lints", padded("name"));
|
||||
println!(" {} ---------", padded("----"));
|
||||
safe_println!(" {} sub-lints", padded("name"));
|
||||
safe_println!(" {} ---------", padded("----"));
|
||||
|
||||
if all_warnings {
|
||||
println!(" {} all lints that are set to issue warnings", padded("warnings"));
|
||||
safe_println!(" {} all lints that are set to issue warnings", padded("warnings"));
|
||||
}
|
||||
|
||||
for (name, to) in lints {
|
||||
@ -929,26 +937,26 @@ Available lint options:
|
||||
.map(|x| x.to_string().replace('_', "-"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
println!(" {} {}", padded(&name), desc);
|
||||
safe_println!(" {} {}", padded(&name), desc);
|
||||
}
|
||||
println!("\n");
|
||||
safe_println!("\n");
|
||||
};
|
||||
|
||||
print_lint_groups(builtin_groups, true);
|
||||
|
||||
match (loaded_plugins, plugin.len(), plugin_groups.len()) {
|
||||
(false, 0, _) | (false, _, 0) => {
|
||||
println!("Lint tools like Clippy can provide additional lints and lint groups.");
|
||||
safe_println!("Lint tools like Clippy can provide additional lints and lint groups.");
|
||||
}
|
||||
(false, ..) => panic!("didn't load lint plugins but got them anyway!"),
|
||||
(true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
|
||||
(true, 0, 0) => safe_println!("This crate does not load any lint plugins or lint groups."),
|
||||
(true, l, g) => {
|
||||
if l > 0 {
|
||||
println!("Lint checks provided by plugins loaded by this crate:\n");
|
||||
safe_println!("Lint checks provided by plugins loaded by this crate:\n");
|
||||
print_lints(plugin);
|
||||
}
|
||||
if g > 0 {
|
||||
println!("Lint groups provided by plugins loaded by this crate:\n");
|
||||
safe_println!("Lint groups provided by plugins loaded by this crate:\n");
|
||||
print_lint_groups(plugin_groups, false);
|
||||
}
|
||||
}
|
||||
@ -996,12 +1004,12 @@ pub fn describe_flag_categories(matches: &Matches) -> bool {
|
||||
}
|
||||
|
||||
fn describe_debug_flags() {
|
||||
println!("\nAvailable options:\n");
|
||||
safe_println!("\nAvailable options:\n");
|
||||
print_flag_list("-Z", config::Z_OPTIONS);
|
||||
}
|
||||
|
||||
fn describe_codegen_flags() {
|
||||
println!("\nAvailable codegen options:\n");
|
||||
safe_println!("\nAvailable codegen options:\n");
|
||||
print_flag_list("-C", config::CG_OPTIONS);
|
||||
}
|
||||
|
||||
@ -1012,7 +1020,7 @@ fn print_flag_list<T>(
|
||||
let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
|
||||
|
||||
for &(name, _, _, desc) in flag_list {
|
||||
println!(
|
||||
safe_println!(
|
||||
" {} {:>width$}=val -- {}",
|
||||
cmdline_opt,
|
||||
name.replace('_', "-"),
|
||||
|
20
compiler/rustc_driver_impl/src/print.rs
Normal file
20
compiler/rustc_driver_impl/src/print.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use std::fmt;
|
||||
use std::io::{self, Write as _};
|
||||
|
||||
macro_rules! safe_print {
|
||||
($($arg:tt)*) => {{
|
||||
$crate::print::print(std::format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! safe_println {
|
||||
($($arg:tt)*) => {
|
||||
safe_print!("{}\n", std::format_args!($($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn print(args: fmt::Arguments<'_>) {
|
||||
if let Err(_) = io::stdout().write_fmt(args) {
|
||||
rustc_errors::FatalError.raise();
|
||||
}
|
||||
}
|
@ -320,7 +320,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
|
||||
};
|
||||
let prohibit_opaque = tcx
|
||||
.explicit_item_bounds(def_id)
|
||||
.iter()
|
||||
.subst_identity_iter_copied()
|
||||
.try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
|
||||
|
||||
if let Some(ty) = prohibit_opaque.break_value() {
|
||||
|
@ -839,7 +839,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx> {
|
||||
});
|
||||
self.types.insert(proj.def_id, (infer_ty, proj.substs));
|
||||
// Recurse into bounds
|
||||
for (pred, pred_span) in self.interner().bound_explicit_item_bounds(proj.def_id).subst_iter_copied(self.interner(), proj.substs) {
|
||||
for (pred, pred_span) in self.interner().explicit_item_bounds(proj.def_id).subst_iter_copied(self.interner(), proj.substs) {
|
||||
let pred = pred.fold_with(self);
|
||||
let pred = self.ocx.normalize(
|
||||
&ObligationCause::misc(self.span, self.body_id),
|
||||
@ -2023,7 +2023,7 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||
};
|
||||
|
||||
let obligations: Vec<_> = tcx
|
||||
.bound_explicit_item_bounds(trait_ty.def_id)
|
||||
.explicit_item_bounds(trait_ty.def_id)
|
||||
.subst_iter_copied(tcx, rebased_substs)
|
||||
.map(|(concrete_ty_bound, span)| {
|
||||
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
|
||||
|
@ -360,7 +360,9 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
|
||||
tcx,
|
||||
param_env,
|
||||
item_def_id,
|
||||
tcx.explicit_item_bounds(item_def_id).to_vec(),
|
||||
tcx.explicit_item_bounds(item_def_id)
|
||||
.subst_identity_iter_copied()
|
||||
.collect::<Vec<_>>(),
|
||||
&FxIndexSet::default(),
|
||||
gat_def_id.def_id,
|
||||
gat_generics,
|
||||
@ -1125,7 +1127,7 @@ fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocIt
|
||||
let bounds = wfcx.tcx().explicit_item_bounds(item.def_id);
|
||||
|
||||
debug!("check_associated_type_bounds: bounds={:?}", bounds);
|
||||
let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
|
||||
let wf_obligations = bounds.subst_identity_iter_copied().flat_map(|(bound, bound_span)| {
|
||||
let normalized_bound = wfcx.normalize(span, None, bound);
|
||||
traits::wf::predicate_obligations(
|
||||
wfcx.infcx,
|
||||
@ -1588,7 +1590,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
|
||||
}
|
||||
});
|
||||
for (bound, bound_span) in tcx
|
||||
.bound_explicit_item_bounds(opaque_ty.def_id)
|
||||
.explicit_item_bounds(opaque_ty.def_id)
|
||||
.subst_iter_copied(tcx, opaque_ty.substs)
|
||||
{
|
||||
let bound = self.wfcx.normalize(bound_span, None, bound);
|
||||
|
@ -79,14 +79,14 @@ fn opaque_type_bounds<'tcx>(
|
||||
pub(super) fn explicit_item_bounds(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
) -> &'_ [(ty::Predicate<'_>, Span)] {
|
||||
) -> ty::EarlyBinder<&'_ [(ty::Predicate<'_>, Span)]> {
|
||||
match tcx.opt_rpitit_info(def_id.to_def_id()) {
|
||||
// RPITIT's bounds are the same as opaque type bounds, but with
|
||||
// a projection self type.
|
||||
Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
|
||||
let item = tcx.hir().get_by_def_id(opaque_def_id.expect_local()).expect_item();
|
||||
let opaque_ty = item.expect_opaque_ty();
|
||||
return opaque_type_bounds(
|
||||
return ty::EarlyBinder(opaque_type_bounds(
|
||||
tcx,
|
||||
opaque_def_id.expect_local(),
|
||||
opaque_ty.bounds,
|
||||
@ -95,7 +95,7 @@ pub(super) fn explicit_item_bounds(
|
||||
ty::InternalSubsts::identity_for_item(tcx, def_id),
|
||||
),
|
||||
item.span,
|
||||
);
|
||||
));
|
||||
}
|
||||
// These should have been fed!
|
||||
Some(ty::ImplTraitInTraitData::Impl { .. }) => unreachable!(),
|
||||
@ -103,7 +103,7 @@ pub(super) fn explicit_item_bounds(
|
||||
}
|
||||
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
match tcx.hir().get(hir_id) {
|
||||
let bounds = match tcx.hir().get(hir_id) {
|
||||
hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Type(bounds, _),
|
||||
span,
|
||||
@ -123,16 +123,18 @@ pub(super) fn explicit_item_bounds(
|
||||
opaque_type_bounds(tcx, def_id, bounds, item_ty, *span)
|
||||
}
|
||||
_ => bug!("item_bounds called on {:?}", def_id),
|
||||
}
|
||||
};
|
||||
ty::EarlyBinder(bounds)
|
||||
}
|
||||
|
||||
pub(super) fn item_bounds(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: DefId,
|
||||
) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
|
||||
let bounds = tcx.mk_predicates_from_iter(util::elaborate(
|
||||
tcx,
|
||||
tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
|
||||
));
|
||||
ty::EarlyBinder(bounds)
|
||||
tcx.explicit_item_bounds(def_id).map_bound(|bounds| {
|
||||
tcx.mk_predicates_from_iter(util::elaborate(
|
||||
tcx,
|
||||
bounds.iter().map(|&(bound, _span)| bound),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -153,8 +153,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
|
||||
let mut collector =
|
||||
OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances };
|
||||
let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id);
|
||||
for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
|
||||
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
|
||||
for (pred, _) in tcx.explicit_item_bounds(item_def_id).subst_iter_copied(tcx, id_substs) {
|
||||
debug!(?pred);
|
||||
|
||||
// We only ignore opaque type substs if the opaque type is the outermost type.
|
||||
|
@ -530,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
for ty in [first_ty, second_ty] {
|
||||
for (pred, _) in self
|
||||
.tcx
|
||||
.bound_explicit_item_bounds(rpit_def_id)
|
||||
.explicit_item_bounds(rpit_def_id)
|
||||
.subst_iter_copied(self.tcx, substs)
|
||||
{
|
||||
let pred = pred.kind().rebind(match pred.kind().skip_binder() {
|
||||
|
@ -172,7 +172,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
|
||||
.deduce_closure_signature_from_predicates(
|
||||
expected_ty,
|
||||
self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
|
||||
self.tcx.explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
|
||||
),
|
||||
ty::Dynamic(ref object_type, ..) => {
|
||||
let sig = object_type.projection_bounds().find_map(|pb| {
|
||||
@ -713,13 +713,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
|
||||
.tcx
|
||||
.bound_explicit_item_bounds(def_id)
|
||||
.explicit_item_bounds(def_id)
|
||||
.subst_iter_copied(self.tcx, substs)
|
||||
.find_map(|(p, s)| get_future_output(p, s))?,
|
||||
ty::Error(_) => return None,
|
||||
ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => self
|
||||
.tcx
|
||||
.bound_explicit_item_bounds(proj.def_id)
|
||||
.explicit_item_bounds(proj.def_id)
|
||||
.subst_iter_copied(self.tcx, proj.substs)
|
||||
.find_map(|(p, s)| get_future_output(p, s))?,
|
||||
_ => span_bug!(
|
||||
|
@ -571,7 +571,7 @@ fn check_must_not_suspend_ty<'tcx>(
|
||||
// FIXME: support adding the attribute to TAITs
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
||||
let mut has_emitted = false;
|
||||
for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) {
|
||||
for &(predicate, _) in fcx.tcx.explicit_item_bounds(def).skip_binder() {
|
||||
// We only look at the `DefId`, so it is safe to skip the binder here.
|
||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) =
|
||||
predicate.kind().skip_binder()
|
||||
|
@ -402,7 +402,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
||||
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
|
||||
|
||||
self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs).find_map(
|
||||
self.tcx.explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs).find_map(
|
||||
|(predicate, _)| {
|
||||
predicate
|
||||
.kind()
|
||||
|
@ -540,7 +540,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.obligations;
|
||||
}
|
||||
|
||||
let item_bounds = tcx.bound_explicit_item_bounds(def_id.to_def_id());
|
||||
let item_bounds = tcx.explicit_item_bounds(def_id);
|
||||
|
||||
for (predicate, _) in item_bounds.subst_iter_copied(tcx, substs) {
|
||||
let predicate = predicate.fold_with(&mut BottomUpFolder {
|
||||
|
@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
|
||||
// For every projection predicate in the opaque type's explicit bounds,
|
||||
// check that the type that we're assigning actually satisfies the bounds
|
||||
// of the associated type.
|
||||
for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
|
||||
for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).subst_identity_iter_copied() {
|
||||
// Liberate bound regions in the predicate since we
|
||||
// don't actually care about lifetimes in this check.
|
||||
let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
|
||||
@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
|
||||
// with `impl Send: OtherTrait`.
|
||||
for (assoc_pred, assoc_pred_span) in cx
|
||||
.tcx
|
||||
.bound_explicit_item_bounds(proj.projection_ty.def_id)
|
||||
.explicit_item_bounds(proj.projection_ty.def_id)
|
||||
.subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
|
||||
{
|
||||
let assoc_pred = assoc_pred.fold_with(proj_replacer);
|
||||
|
@ -254,7 +254,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
||||
}
|
||||
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
||||
elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned())
|
||||
elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).subst_identity_iter_copied())
|
||||
// We only care about self bounds for the impl-trait
|
||||
.filter_only_self()
|
||||
.find_map(|(pred, _span)| {
|
||||
|
@ -23,7 +23,7 @@ use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
|
||||
use rustc_middle::ty::codec::TyDecoder;
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::GeneratorDiagnosticData;
|
||||
use rustc_middle::ty::{self, ParameterizedOverTcx, Ty, TyCtxt, Visibility};
|
||||
use rustc_middle::ty::{self, ParameterizedOverTcx, Predicate, Ty, TyCtxt, Visibility};
|
||||
use rustc_serialize::opaque::MemDecoder;
|
||||
use rustc_serialize::{Decodable, Decoder};
|
||||
use rustc_session::cstore::{
|
||||
@ -857,6 +857,20 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
fn get_explicit_item_bounds(
|
||||
self,
|
||||
index: DefIndex,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> ty::EarlyBinder<&'tcx [(Predicate<'tcx>, Span)]> {
|
||||
let lazy = self.root.tables.explicit_item_bounds.get(self, index);
|
||||
let output = if lazy.is_default() {
|
||||
&mut []
|
||||
} else {
|
||||
tcx.arena.alloc_from_iter(lazy.decode((self, tcx)))
|
||||
};
|
||||
ty::EarlyBinder(&*output)
|
||||
}
|
||||
|
||||
fn get_variant(self, kind: &DefKind, index: DefIndex, parent_did: DefId) -> ty::VariantDef {
|
||||
let adt_kind = match kind {
|
||||
DefKind::Variant => ty::AdtKind::Enum,
|
||||
|
@ -203,7 +203,7 @@ impl IntoArgs for (CrateNum, SimplifiedType) {
|
||||
}
|
||||
|
||||
provide! { tcx, def_id, other, cdata,
|
||||
explicit_item_bounds => { table_defaulted_array }
|
||||
explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) }
|
||||
explicit_predicates_of => { table }
|
||||
generics_of => { table }
|
||||
inferred_outlives_of => { table_defaulted_array }
|
||||
|
@ -1423,7 +1423,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
|
||||
fn encode_explicit_item_bounds(&mut self, def_id: DefId) {
|
||||
debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id);
|
||||
let bounds = self.tcx.explicit_item_bounds(def_id);
|
||||
let bounds = self.tcx.explicit_item_bounds(def_id).skip_binder();
|
||||
record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds);
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@
|
||||
#![feature(const_option)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(ptr_alignment_type)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![recursion_limit = "512"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
||||
|
@ -251,7 +251,7 @@ rustc_queries! {
|
||||
/// `key` is the `DefId` of the associated type or opaque type.
|
||||
///
|
||||
/// Bounds from the parent (e.g. with nested impl trait) are not included.
|
||||
query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
|
||||
query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Predicate<'tcx>, Span)]> {
|
||||
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { key.is_local() }
|
||||
separate_provide_extern
|
||||
|
@ -1603,7 +1603,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false };
|
||||
let future_trait = self.require_lang_item(LangItem::Future, None);
|
||||
|
||||
self.explicit_item_bounds(def_id).iter().any(|(predicate, _)| {
|
||||
self.explicit_item_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| {
|
||||
let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() else {
|
||||
return false;
|
||||
};
|
||||
|
@ -861,6 +861,11 @@ impl<'tcx> PolyTraitPredicate<'tcx> {
|
||||
pub fn is_const_if_const(self) -> bool {
|
||||
self.skip_binder().is_const_if_const()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn polarity(self) -> ImplPolarity {
|
||||
self.skip_binder().polarity
|
||||
}
|
||||
}
|
||||
|
||||
/// `A: B`
|
||||
@ -1497,29 +1502,12 @@ struct ParamTag {
|
||||
constness: hir::Constness,
|
||||
}
|
||||
|
||||
unsafe impl rustc_data_structures::tagged_ptr::Tag for ParamTag {
|
||||
const BITS: u32 = 2;
|
||||
|
||||
#[inline]
|
||||
fn into_usize(self) -> usize {
|
||||
match self {
|
||||
Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } => 0,
|
||||
Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } => 1,
|
||||
Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } => 2,
|
||||
Self { reveal: traits::Reveal::All, constness: hir::Constness::Const } => 3,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_usize(ptr: usize) -> Self {
|
||||
match ptr {
|
||||
0 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst },
|
||||
1 => Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst },
|
||||
2 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const },
|
||||
3 => Self { reveal: traits::Reveal::All, constness: hir::Constness::Const },
|
||||
_ => std::hint::unreachable_unchecked(),
|
||||
}
|
||||
}
|
||||
impl_tag! {
|
||||
impl Tag for ParamTag;
|
||||
ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst },
|
||||
ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::NotConst },
|
||||
ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const },
|
||||
ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::Const },
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Debug for ParamEnv<'tcx> {
|
||||
|
@ -914,7 +914,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||
|
||||
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
|
||||
// by looking up the projections associated with the def_id.
|
||||
let bounds = tcx.bound_explicit_item_bounds(def_id);
|
||||
let bounds = tcx.explicit_item_bounds(def_id);
|
||||
|
||||
let mut traits = FxIndexMap::default();
|
||||
let mut fn_traits = FxIndexMap::default();
|
||||
|
@ -612,6 +612,12 @@ where
|
||||
) -> SubstIter<'s, 'tcx, I> {
|
||||
SubstIter { it: self.0.into_iter(), tcx, substs }
|
||||
}
|
||||
|
||||
/// Similar to [`subst_identity`](EarlyBinder::subst_identity),
|
||||
/// but on an iterator of `TypeFoldable` values.
|
||||
pub fn subst_identity_iter(self) -> I::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SubstIter<'s, 'tcx, I: IntoIterator> {
|
||||
@ -664,6 +670,12 @@ where
|
||||
) -> SubstIterCopied<'s, 'tcx, I> {
|
||||
SubstIterCopied { it: self.0.into_iter(), tcx, substs }
|
||||
}
|
||||
|
||||
/// Similar to [`subst_identity`](EarlyBinder::subst_identity),
|
||||
/// but on an iterator of values that deref to a `TypeFoldable`.
|
||||
pub fn subst_identity_iter_copied(self) -> impl Iterator<Item = <I::Item as Deref>::Target> {
|
||||
self.0.into_iter().map(|v| *v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SubstIterCopied<'a, 'tcx, I: IntoIterator> {
|
||||
|
@ -701,13 +701,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
|
||||
}
|
||||
|
||||
pub fn bound_explicit_item_bounds(
|
||||
self,
|
||||
def_id: DefId,
|
||||
) -> ty::EarlyBinder<&'tcx [(ty::Predicate<'tcx>, rustc_span::Span)]> {
|
||||
ty::EarlyBinder(self.explicit_item_bounds(def_id))
|
||||
}
|
||||
|
||||
/// Returns names of captured upvars for closures and generators.
|
||||
///
|
||||
/// Here are some examples:
|
||||
|
@ -1800,7 +1800,7 @@ fn check_must_not_suspend_ty<'tcx>(
|
||||
// FIXME: support adding the attribute to TAITs
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
||||
let mut has_emitted = false;
|
||||
for &(predicate, _) in tcx.explicit_item_bounds(def) {
|
||||
for &(predicate, _) in tcx.explicit_item_bounds(def).skip_binder() {
|
||||
// We only look at the `DefId`, so it is safe to skip the binder here.
|
||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) =
|
||||
predicate.kind().skip_binder()
|
||||
|
@ -269,7 +269,7 @@ where
|
||||
// and are visited by shallow visitors.
|
||||
self.visit_predicates(ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: tcx.explicit_item_bounds(def_id),
|
||||
predicates: tcx.explicit_item_bounds(def_id).skip_binder(),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
@ -1784,7 +1784,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
|
||||
fn bounds(&mut self) -> &mut Self {
|
||||
self.visit_predicates(ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: self.tcx.explicit_item_bounds(self.item_def_id),
|
||||
predicates: self.tcx.explicit_item_bounds(self.item_def_id).skip_binder(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
@ -86,8 +86,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
|
||||
&& poly_trait_pred.def_id() == goal.predicate.def_id()
|
||||
&& poly_trait_pred.polarity() == goal.predicate.polarity
|
||||
{
|
||||
// FIXME: Constness and polarity
|
||||
// FIXME: Constness
|
||||
ecx.probe(|ecx| {
|
||||
let assumption_trait_pred =
|
||||
ecx.instantiate_binder_with_infer(poly_trait_pred);
|
||||
@ -111,6 +112,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
|
||||
&& poly_trait_pred.def_id() == goal.predicate.def_id()
|
||||
&& poly_trait_pred.polarity() == goal.predicate.polarity
|
||||
{
|
||||
// FIXME: Constness and polarity
|
||||
ecx.probe(|ecx| {
|
||||
@ -147,6 +149,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
|
||||
return result;
|
||||
}
|
||||
@ -161,6 +167,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
|
||||
ecx.probe(|ecx| {
|
||||
@ -176,6 +186,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
||||
goal,
|
||||
structural_traits::instantiate_constituent_tys_for_sized_trait,
|
||||
@ -186,6 +200,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
||||
goal,
|
||||
structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
|
||||
@ -196,6 +214,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
if goal.predicate.self_ty().has_non_region_infer() {
|
||||
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
||||
}
|
||||
@ -217,6 +239,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
if let ty::FnPtr(..) = goal.predicate.self_ty().kind() {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
@ -229,6 +255,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
goal: Goal<'tcx, Self>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
let tupled_inputs_and_output =
|
||||
match structural_traits::extract_tupled_inputs_and_output_from_callable(
|
||||
@ -259,6 +289,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
@ -268,8 +302,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
|
||||
fn consider_builtin_pointee_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
_goal: Goal<'tcx, Self>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
@ -277,6 +315,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
|
||||
return Err(NoSolution);
|
||||
};
|
||||
@ -297,6 +339,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
|
||||
return Err(NoSolution);
|
||||
@ -326,6 +372,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
let a_ty = goal.predicate.self_ty();
|
||||
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
|
||||
@ -447,6 +497,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> Vec<CanonicalResponse<'tcx>> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
|
||||
let a_ty = goal.predicate.self_ty();
|
||||
@ -521,8 +575,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
|
||||
fn consider_builtin_discriminant_kind_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
_goal: Goal<'tcx, Self>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// `DiscriminantKind` is automatically implemented for every type.
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
@ -531,6 +589,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
if !goal.param_env.is_const() {
|
||||
// `Destruct` is automatically implemented for every type in
|
||||
// non-const environments.
|
||||
@ -545,6 +607,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// `rustc_transmute` does not have support for type or const params
|
||||
if goal.has_non_region_placeholders() {
|
||||
return Err(NoSolution);
|
||||
|
@ -297,8 +297,8 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
|
||||
tcx.associated_items(trait_def_id)
|
||||
.in_definition_order()
|
||||
.filter(|item| item.kind == ty::AssocKind::Type)
|
||||
.flat_map(|item| tcx.explicit_item_bounds(item.def_id))
|
||||
.filter_map(|pred_span| predicate_references_self(tcx, *pred_span))
|
||||
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).subst_identity_iter_copied())
|
||||
.filter_map(|pred_span| predicate_references_self(tcx, pred_span))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -50,12 +50,11 @@ impl<'tcx> RustIrDatabase<'tcx> {
|
||||
where
|
||||
ty::Predicate<'tcx>: LowerInto<'tcx, std::option::Option<T>>,
|
||||
{
|
||||
let bounds = self.interner.tcx.bound_explicit_item_bounds(def_id);
|
||||
bounds
|
||||
.0
|
||||
.iter()
|
||||
.map(|(bound, _)| bounds.rebind(*bound).subst(self.interner.tcx, &bound_vars))
|
||||
.filter_map(|bound| LowerInto::<Option<_>>::lower_into(bound, self.interner))
|
||||
self.interner
|
||||
.tcx
|
||||
.explicit_item_bounds(def_id)
|
||||
.subst_iter_copied(self.interner.tcx, &bound_vars)
|
||||
.filter_map(|(bound, _)| LowerInto::<Option<_>>::lower_into(bound, self.interner))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@ -506,15 +505,11 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
|
||||
|
||||
let identity_substs = InternalSubsts::identity_for_item(self.interner.tcx, opaque_ty_id.0);
|
||||
|
||||
let explicit_item_bounds = self.interner.tcx.bound_explicit_item_bounds(opaque_ty_id.0);
|
||||
let explicit_item_bounds = self.interner.tcx.explicit_item_bounds(opaque_ty_id.0);
|
||||
let bounds =
|
||||
explicit_item_bounds
|
||||
.0
|
||||
.iter()
|
||||
.subst_iter_copied(self.interner.tcx, &bound_vars)
|
||||
.map(|(bound, _)| {
|
||||
explicit_item_bounds.rebind(*bound).subst(self.interner.tcx, &bound_vars)
|
||||
})
|
||||
.map(|bound| {
|
||||
bound.fold_with(&mut ReplaceOpaqueTyFolder {
|
||||
tcx: self.interner.tcx,
|
||||
opaque_ty_id,
|
||||
|
@ -39,6 +39,7 @@ use crate::sync::Arc;
|
||||
/// use std::sync::Arc;
|
||||
/// use std::task::{Context, Poll, Wake};
|
||||
/// use std::thread::{self, Thread};
|
||||
/// use core::pin::pin;
|
||||
///
|
||||
/// /// A waker that wakes up the current thread when called.
|
||||
/// struct ThreadWaker(Thread);
|
||||
@ -52,7 +53,7 @@ use crate::sync::Arc;
|
||||
/// /// Run a future to completion on the current thread.
|
||||
/// fn block_on<T>(fut: impl Future<Output = T>) -> T {
|
||||
/// // Pin the future so it can be polled.
|
||||
/// let mut fut = Box::pin(fut);
|
||||
/// let mut fut = pin!(fut);
|
||||
///
|
||||
/// // Create a new context to be passed to the future.
|
||||
/// let t = thread::current();
|
||||
|
@ -1456,7 +1456,6 @@ pub struct TimSortRun {
|
||||
|
||||
/// Takes a range as denoted by start and end, that is already sorted and extends it to the right if
|
||||
/// necessary with sorts optimized for smaller ranges such as insertion sort.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
fn provide_sorted_batch<T, F>(v: &mut [T], start: usize, mut end: usize, is_less: &mut F) -> usize
|
||||
where
|
||||
F: FnMut(&T, &T) -> bool,
|
||||
|
@ -422,8 +422,8 @@ fn clean_projection<'tcx>(
|
||||
let bounds = cx
|
||||
.tcx
|
||||
.explicit_item_bounds(ty.skip_binder().def_id)
|
||||
.iter()
|
||||
.map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.skip_binder().substs))
|
||||
.subst_iter_copied(cx.tcx, ty.skip_binder().substs)
|
||||
.map(|(pred, _)| pred)
|
||||
.collect::<Vec<_>>();
|
||||
return clean_middle_opaque_bounds(cx, bounds);
|
||||
}
|
||||
@ -1315,10 +1315,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
||||
}
|
||||
|
||||
if let ty::TraitContainer = assoc_item.container {
|
||||
let bounds = tcx.explicit_item_bounds(assoc_item.def_id);
|
||||
let bounds =
|
||||
tcx.explicit_item_bounds(assoc_item.def_id).subst_identity_iter_copied();
|
||||
let predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates;
|
||||
let predicates =
|
||||
tcx.arena.alloc_from_iter(bounds.into_iter().chain(predicates).copied());
|
||||
tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied()));
|
||||
let mut generics = clean_ty_generics(
|
||||
cx,
|
||||
tcx.generics_of(assoc_item.def_id),
|
||||
@ -1845,8 +1846,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
||||
let bounds = cx
|
||||
.tcx
|
||||
.explicit_item_bounds(def_id)
|
||||
.iter()
|
||||
.map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, substs))
|
||||
.subst_iter_copied(cx.tcx, substs)
|
||||
.map(|(bound, _)| bound)
|
||||
.collect::<Vec<_>>();
|
||||
clean_middle_opaque_bounds(cx, bounds)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, AliasTy, Clause, EarlyBinder, PredicateKind};
|
||||
use rustc_middle::ty::{self, AliasTy, Clause, PredicateKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
@ -66,8 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
||||
if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() {
|
||||
let preds = cx.tcx.explicit_item_bounds(def_id);
|
||||
let mut is_future = false;
|
||||
for &(p, _span) in preds {
|
||||
let p = EarlyBinder(p).subst(cx.tcx, substs);
|
||||
for (p, _span) in preds.subst_iter_copied(cx.tcx, substs) {
|
||||
if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
|
||||
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
|
||||
is_future = true;
|
||||
|
@ -90,7 +90,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
|
||||
return false;
|
||||
}
|
||||
|
||||
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
|
||||
for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).subst_identity_iter_copied() {
|
||||
match predicate.kind().skip_binder() {
|
||||
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
|
||||
// and check substituions to find `U`.
|
||||
@ -267,7 +267,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
},
|
||||
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
|
||||
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
|
||||
for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() {
|
||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() {
|
||||
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
|
||||
return true;
|
||||
@ -743,7 +743,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
|
||||
|
||||
for (pred, _) in cx
|
||||
.tcx
|
||||
.bound_explicit_item_bounds(ty.def_id)
|
||||
.explicit_item_bounds(ty.def_id)
|
||||
.subst_iter_copied(cx.tcx, ty.substs)
|
||||
{
|
||||
match pred.kind().skip_binder() {
|
||||
|
40
tests/ui/traits/new-solver/negative-coherence-bounds.rs
Normal file
40
tests/ui/traits/new-solver/negative-coherence-bounds.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// check-pass
|
||||
|
||||
// This test verifies that negative trait predicate cannot be satisfied from a
|
||||
// positive param-env candidate.
|
||||
|
||||
// Negative coherence is one of the only places where we actually construct and
|
||||
// evaluate negative predicates. Specifically, when verifying whether the first
|
||||
// and second impls below overlap, we do not want to consider them disjoint,
|
||||
// otherwise the second impl would be missing an associated type `type Item`
|
||||
// which is provided by the first impl that it is specializing.
|
||||
|
||||
#![feature(specialization)]
|
||||
//~^ WARN the feature `specialization` is incomplete
|
||||
#![feature(with_negative_coherence)]
|
||||
|
||||
trait BoxIter {
|
||||
type Item;
|
||||
|
||||
fn last(self) -> Option<Self::Item>;
|
||||
}
|
||||
|
||||
impl<I: Iterator + ?Sized> BoxIter for Box<I> {
|
||||
type Item = I::Item;
|
||||
|
||||
default fn last(self) -> Option<I::Item> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// When checking that this impl does/doesn't overlap the one above, we evaluate
|
||||
// a negative version of all of the where-clause predicates of the impl below.
|
||||
// For `I: !Iterator`, we should make sure that the param-env clause `I: Iterator`
|
||||
// from above doesn't satisfy this predicate.
|
||||
impl<I: Iterator> BoxIter for Box<I> {
|
||||
fn last(self) -> Option<I::Item> {
|
||||
(*self).last()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
12
tests/ui/traits/new-solver/negative-coherence-bounds.stderr
Normal file
12
tests/ui/traits/new-solver/negative-coherence-bounds.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/negative-coherence-bounds.rs:12:12
|
||||
|
|
||||
LL | #![feature(specialization)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
|
||||
= help: consider using `min_specialization` instead, which is more stable and complete
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
Loading…
Reference in New Issue
Block a user