From 799fa60c07784bb033d1e1bcdcc7f671ea47d617 Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Sat, 24 Dec 2022 08:19:20 +0000 Subject: [PATCH 01/14] llvm-wrapper: adapt for LLVM API change No functional changes intended. The LLVM commit https://github.com/llvm/llvm-project/commit/e6b02214c68df2c9f826e02310c9352ac652e456 added `TargetExtTyID` to the `TypeID` enum. This adapts `RustWrapper` accordingly. --- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 279b699185421..a26da9d548fb6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1349,18 +1349,16 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { return LLVMBFloatTypeKind; case Type::X86_AMXTyID: return LLVMX86_AMXTypeKind; -#if LLVM_VERSION_GE(15, 0) && LLVM_VERSION_LT(16, 0) - case Type::DXILPointerTyID: - report_fatal_error("Rust does not support DirectX typed pointers."); - break; -#endif -#if LLVM_VERSION_GE(16, 0) - case Type::TypedPointerTyID: - report_fatal_error("Rust does not support typed pointers."); - break; -#endif + default: + { + std::string error; + llvm::raw_string_ostream stream(error); + stream << "Rust does not support the TypeID: " << unwrap(Ty)->getTypeID() + << " for the type: " << *unwrap(Ty); + stream.flush(); + report_fatal_error(error.c_str()); + } } - report_fatal_error("Unhandled TypeID."); } DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) From 13e25b82f0d3afc7bcfc6cd1617e8e4287172960 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 25 Dec 2022 18:10:11 -0500 Subject: [PATCH 02/14] Improve the documentation of `black_box` --- library/core/src/hint.rs | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index e8d724ab1ef4e..5a76e86692336 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -219,6 +219,75 @@ pub fn spin_loop() { /// backend used. Programs cannot rely on `black_box` for *correctness* in any way. /// /// [`std::convert::identity`]: crate::convert::identity +/// +/// # When is this useful? +/// +/// First and foremost: `black_box` does _not_ guarantee any exact behavior and, in some cases, may +/// do nothing at all. As such, it **must not be relied upon to control critical program behavior.** +/// This _immediately_ precludes any direct use of this function for cryptographic or security +/// purposes. +/// +/// While not suitable in those mission-critical cases, `back_box`'s functionality can generally be +/// relied upon for benchmarking, and should be used there. It will try to ensure that the +/// compiler doesn't optimize away part of the intended test code based on context. For +/// example: +/// +/// ``` +/// fn contains(haystack: &[&str], needle: &str) -> bool { +/// haystack.iter().any(|x| x == &needle) +/// } +/// +/// pub fn benchmark() { +/// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"]; +/// let needle = "ghi"; +/// for _ in 0..10 { +/// contains(&haystack, needle); +/// } +/// } +/// ``` +/// +/// The compiler could theoretically make optimizations like the following: +/// +/// - `needle` and `haystack` are always the same, move the call to `contains` outside the loop and +/// delete the loop +/// - Inline `contains` +/// - `needle` and `haystack` have values known at compile time, `contains` is always true. Remove +/// the call and replace with `true` +/// - Nothing is done with the result of `contains`: delete this function call entirely +/// - `benchmark` now has no purpose: delete this function +/// +/// It is not likely that all of the above happens, but the compiler is definitely able to make some +/// optimizations that could result in a very inaccurate benchmark. This is where `black_box` comes +/// in: +/// +/// ``` +/// use std::hint::black_box; +/// +/// // Same `contains` function +/// fn contains(haystack: &[&str], needle: &str) -> bool { +/// haystack.iter().any(|x| x == &needle) +/// } +/// +/// pub fn benchmark() { +/// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"]; +/// let needle = "ghi"; +/// for _ in 0..10 { +/// // Adjust our benchmark loop contents +/// black_box(contains(black_box(&haystack), black_box(needle))); +/// } +/// } +/// ``` +/// +/// This essentially tells the compiler to block optimizations across any calls to `black_box`. So, +/// it now: +/// +/// - Treats both arguments to `contains` as unpredictable: the body of `contains` can no longer be +/// optimized based on argument values +/// - Treats the call to `contains` and its result as volatile: the body of `benchmark` cannot +/// optimize this away +/// +/// This makes our benchmark much more realistic to how the function would be used in situ, where +/// arguments are usually not known at compile time and the result is used in some way. #[inline] #[stable(feature = "bench_black_box", since = "1.66.0")] #[rustc_const_unstable(feature = "const_black_box", issue = "none")] From fe96c11aba8ef34de187551572a8943d89ae26ac Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 16 Jan 2023 21:06:34 +0900 Subject: [PATCH 03/14] fix #104440 --- compiler/rustc_lint/src/context.rs | 47 ++++---- compiler/rustc_lint_defs/src/lib.rs | 2 +- .../rustc_resolve/src/late/diagnostics.rs | 22 +++- tests/ui/single-use-lifetime/issue-104440.rs | 100 ++++++++++++++++++ .../single-use-lifetime/issue-104440.stderr | 28 +++++ 5 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 tests/ui/single-use-lifetime/issue-104440.rs create mode 100644 tests/ui/single-use-lifetime/issue-104440.stderr diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index c9b9a62257148..8046cc21cea58 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -825,21 +825,24 @@ pub trait LintContext: Sized { debug!(?param_span, ?use_span, ?deletion_span); db.span_label(param_span, "this lifetime..."); db.span_label(use_span, "...is used only here"); - let msg = "elide the single-use lifetime"; - let (use_span, replace_lt) = if elide { - let use_span = sess.source_map().span_extend_while( - use_span, - char::is_whitespace, - ).unwrap_or(use_span); - (use_span, String::new()) - } else { - (use_span, "'_".to_owned()) - }; - db.multipart_suggestion( - msg, - vec![(deletion_span, String::new()), (use_span, replace_lt)], - Applicability::MachineApplicable, - ); + if let Some(deletion_span) = deletion_span { + let msg = "elide the single-use lifetime"; + let (use_span, replace_lt) = if elide { + let use_span = sess.source_map().span_extend_while( + use_span, + char::is_whitespace, + ).unwrap_or(use_span); + (use_span, String::new()) + } else { + (use_span, "'_".to_owned()) + }; + debug!(?deletion_span, ?use_span); + db.multipart_suggestion( + msg, + vec![(deletion_span, String::new()), (use_span, replace_lt)], + Applicability::MachineApplicable, + ); + } }, BuiltinLintDiagnostics::SingleUseLifetime { param_span: _, @@ -847,12 +850,14 @@ pub trait LintContext: Sized { deletion_span, } => { debug!(?deletion_span); - db.span_suggestion( - deletion_span, - "elide the unused lifetime", - "", - Applicability::MachineApplicable, - ); + if let Some(deletion_span) = deletion_span { + db.span_suggestion( + deletion_span, + "elide the unused lifetime", + "", + Applicability::MachineApplicable, + ); + } }, BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => { db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string"); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index f4b4c5168bfd6..45e2467340bab 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -502,7 +502,7 @@ pub enum BuiltinLintDiagnostics { param_span: Span, /// Span of the code that should be removed when eliding this lifetime. /// This span should include leading or trailing comma. - deletion_span: Span, + deletion_span: Option, /// Span of the single use, or None if the lifetime is never used. /// If true, the lifetime will be fully elided. use_span: Option<(Span, bool)>, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d92b046d0b9f2..6d448433ee6db 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2188,15 +2188,31 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let deletion_span = || { if params.len() == 1 { // if sole lifetime, remove the entire `<>` brackets - generics_span + Some(generics_span) } else if param_index == 0 { // if removing within `<>` brackets, we also want to // delete a leading or trailing comma as appropriate - param.span().to(params[param_index + 1].span().shrink_to_lo()) + match ( + param.span().find_ancestor_inside(generics_span), + params[param_index + 1].span().find_ancestor_inside(generics_span), + ) { + (Some(param_span), Some(next_param_span)) => { + Some(param_span.to(next_param_span.shrink_to_lo())) + } + _ => None, + } } else { // if removing within `<>` brackets, we also want to // delete a leading or trailing comma as appropriate - params[param_index - 1].span().shrink_to_hi().to(param.span()) + match ( + param.span().find_ancestor_inside(generics_span), + params[param_index - 1].span().find_ancestor_inside(generics_span), + ) { + (Some(param_span), Some(prev_param_span)) => { + Some(prev_param_span.shrink_to_hi().to(param_span)) + } + _ => None, + } } }; match use_set { diff --git a/tests/ui/single-use-lifetime/issue-104440.rs b/tests/ui/single-use-lifetime/issue-104440.rs new file mode 100644 index 0000000000000..0795e95303a1e --- /dev/null +++ b/tests/ui/single-use-lifetime/issue-104440.rs @@ -0,0 +1,100 @@ +#![feature(decl_macro, rustc_attrs)] +#![deny(single_use_lifetimes)] + +mod type_params { + macro m($T:ident) { + fn f<$T: Clone, T: PartialEq>(t1: $T, t2: T) -> ($T, bool) { + (t1.clone(), t2 == t2) + } + } + + #[rustc_macro_transparency = "semitransparent"] + macro n($T:ident) { + fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + fn h(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + } + + #[rustc_macro_transparency = "transparent"] + macro p($T:ident) { + fn j<$T: Clone>(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + fn k(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + } + + m!(T); + n!(T); + p!(T); +} + +mod lifetime_params { + macro m($a:lifetime) { + fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once + (t1, t2) + } + } + + #[rustc_macro_transparency = "semitransparent"] + macro n($a:lifetime) { + fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + fn h<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "transparent"] + macro p($a:lifetime) { + fn j<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + } + + m!('a); //~ ERROR lifetime parameter `'a` only used once + n!('a); + p!('a); +} + +mod const_params { + macro m($C:ident) { + fn f(t1: [(); $C], t2: [(); C]) -> ([(); $C], [(); C]) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "semitransparent"] + macro n($C:ident) { + fn g(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + fn h(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "transparent"] + macro p($C:ident) { + fn j(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + fn k(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + } + + m!(C); + n!(C); + p!(C); +} + +fn main() {} diff --git a/tests/ui/single-use-lifetime/issue-104440.stderr b/tests/ui/single-use-lifetime/issue-104440.stderr new file mode 100644 index 0000000000000..54ded31dcbe89 --- /dev/null +++ b/tests/ui/single-use-lifetime/issue-104440.stderr @@ -0,0 +1,28 @@ +error: lifetime parameter `'a` only used once + --> $DIR/issue-104440.rs:63:8 + | +LL | m!('a); + | ^^ + | | + | this lifetime... + | ...is used only here + | +note: the lint level is defined here + --> $DIR/issue-104440.rs:2:9 + | +LL | #![deny(single_use_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: lifetime parameter `'a` only used once + --> $DIR/issue-104440.rs:38:30 + | +LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { + | ^^ this lifetime... -- ...is used only here +... +LL | m!('a); + | ------ in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + From f6d8abfcbfe2e2600f52bef27d8677febf72767a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 17 Jan 2023 18:15:54 -0800 Subject: [PATCH 04/14] Re-enable building rust-analyzer on riscv64 It was disabled in #75103 due to an LLVM bug, but followup comments have confirmed that it builds fine on Fedora with LLVM 15. --- src/bootstrap/dist.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 68215790bed17..2e4f753965ded 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1130,12 +1130,6 @@ impl Step for RustAnalyzer { let compiler = self.compiler; let target = self.target; - if target.contains("riscv64") { - // riscv64 currently has an LLVM bug that makes rust-analyzer unable - // to build. See #74813 for details. - return None; - } - let rust_analyzer = builder .ensure(tool::RustAnalyzer { compiler, target }) .expect("rust-analyzer always builds"); From fa7d17dbb570d1674781c590849ea22ebc882418 Mon Sep 17 00:00:00 2001 From: Albert Larsan <74931857+albertlarsan68@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:15:57 +0000 Subject: [PATCH 05/14] Create new bootstrap team --- triagebot.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index bfbbae6857fa8..16a3132151374 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -175,7 +175,7 @@ exclude_labels = [ "T-*", ] -[autolabel."A-bootstrap"] +[autolabel."T-bootstrap"] trigger_files = [ "x.py", "x", @@ -493,6 +493,8 @@ libs = [ ] bootstrap = [ "@Mark-Simulacrum", + "@albertlarsan68", + "@ozkanonur", ] infra-ci = [ "@Mark-Simulacrum", From d3cfe97a8a975511a631083d64aa6ae3ddf7470a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 19 Jan 2023 00:00:00 +0000 Subject: [PATCH 06/14] Custom MIR: Support binary and unary operations --- .../src/build/custom/parse/instruction.rs | 6 ++++ .../custom/operators.f.built.after.mir | 26 +++++++++++++++++ tests/mir-opt/building/custom/operators.rs | 28 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/mir-opt/building/custom/operators.f.built.after.mir create mode 100644 tests/mir-opt/building/custom/operators.rs diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index dca4906c07de5..2560eaee43372 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -141,6 +141,12 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ExprKind::AddressOf { mutability, arg } => Ok( Rvalue::AddressOf(*mutability, self.parse_place(*arg)?) ), + ExprKind::Binary { op, lhs, rhs } => Ok( + Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) + ), + ExprKind::Unary { op, arg } => Ok( + Rvalue::UnaryOp(*op, self.parse_operand(*arg)?) + ), _ => self.parse_operand(expr_id).map(Rvalue::Use), ) } diff --git a/tests/mir-opt/building/custom/operators.f.built.after.mir b/tests/mir-opt/building/custom/operators.f.built.after.mir new file mode 100644 index 0000000000000..a0c5f1b40dbb7 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.f.built.after.mir @@ -0,0 +1,26 @@ +// MIR for `f` after built + +fn f(_1: i32, _2: bool) -> i32 { + let mut _0: i32; // return place in scope 0 at $DIR/operators.rs:+0:30: +0:33 + + bb0: { + _1 = Neg(_1); // scope 0 at $DIR/operators.rs:+2:9: +2:15 + _2 = Not(_2); // scope 0 at $DIR/operators.rs:+3:9: +3:15 + _1 = Add(_1, _1); // scope 0 at $DIR/operators.rs:+4:9: +4:18 + _1 = Sub(_1, _1); // scope 0 at $DIR/operators.rs:+5:9: +5:18 + _1 = Mul(_1, _1); // scope 0 at $DIR/operators.rs:+6:9: +6:18 + _1 = Div(_1, _1); // scope 0 at $DIR/operators.rs:+7:9: +7:18 + _1 = Rem(_1, _1); // scope 0 at $DIR/operators.rs:+8:9: +8:18 + _1 = BitXor(_1, _1); // scope 0 at $DIR/operators.rs:+9:9: +9:18 + _1 = BitAnd(_1, _1); // scope 0 at $DIR/operators.rs:+10:9: +10:18 + _1 = Shl(_1, _1); // scope 0 at $DIR/operators.rs:+11:9: +11:19 + _1 = Shr(_1, _1); // scope 0 at $DIR/operators.rs:+12:9: +12:19 + _2 = Eq(_1, _1); // scope 0 at $DIR/operators.rs:+13:9: +13:19 + _2 = Lt(_1, _1); // scope 0 at $DIR/operators.rs:+14:9: +14:18 + _2 = Le(_1, _1); // scope 0 at $DIR/operators.rs:+15:9: +15:19 + _2 = Ge(_1, _1); // scope 0 at $DIR/operators.rs:+16:9: +16:19 + _2 = Gt(_1, _1); // scope 0 at $DIR/operators.rs:+17:9: +17:18 + _0 = _1; // scope 0 at $DIR/operators.rs:+18:9: +18:16 + return; // scope 0 at $DIR/operators.rs:+19:9: +19:17 + } +} diff --git a/tests/mir-opt/building/custom/operators.rs b/tests/mir-opt/building/custom/operators.rs new file mode 100644 index 0000000000000..51f80c66392a1 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.rs @@ -0,0 +1,28 @@ +// compile-flags: --crate-type=lib +#![feature(custom_mir, core_intrinsics, inline_const)] +use std::intrinsics::mir::*; + +// EMIT_MIR operators.f.built.after.mir +#[custom_mir(dialect = "built")] +pub fn f(a: i32, b: bool) -> i32 { + mir!({ + a = -a; + b = !b; + a = a + a; + a = a - a; + a = a * a; + a = a / a; + a = a % a; + a = a ^ a; + a = a & a; + a = a << a; + a = a >> a; + b = a == a; + b = a < a; + b = a <= a; + b = a >= a; + b = a > a; + RET = a; + Return() + }) +} From ec3da87582a318ddb35d451bdba6d50591ea55ce Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Fri, 20 Jan 2023 11:16:38 +0100 Subject: [PATCH 07/14] Add note about absolute paths to Path::join --- library/std/src/path.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index c3593264e520b..500a09ad08abc 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2531,6 +2531,8 @@ impl Path { /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. /// + /// If `path` is absolute, it replaces the current path. + /// /// See [`PathBuf::push`] for more details on what it means to adjoin a path. /// /// # Examples From d8f8adfe3df3cdc882db8ba7dfb9d7fcc8533b82 Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Fri, 20 Jan 2023 12:03:43 +0100 Subject: [PATCH 08/14] add example of joining with a absolute path --- library/std/src/path.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 500a09ad08abc..2f53cf8393691 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2541,6 +2541,7 @@ impl Path { /// use std::path::{Path, PathBuf}; /// /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use] From 8742fd9c8592a846d487e8a1cdab8235859c4bc8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 8 Jan 2023 00:29:30 +0000 Subject: [PATCH 09/14] Label closure captures/generator locals that make opaque types recursive --- .../rustc_hir_analysis/src/check/check.rs | 59 +++++++++++++++++-- tests/ui/impl-trait/recursive-generator.rs | 23 ++++++++ .../ui/impl-trait/recursive-generator.stderr | 19 ++++++ .../recursive-impl-trait-type-indirect.stderr | 4 ++ 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 tests/ui/impl-trait/recursive-generator.rs create mode 100644 tests/ui/impl-trait/recursive-generator.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index aa75132444a5a..abc1c2d7b8d17 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1391,11 +1391,15 @@ fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed /// /// If all the return expressions evaluate to `!`, then we explain that the error will go away /// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. -fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed { +fn opaque_type_cycle_error( + tcx: TyCtxt<'_>, + opaque_def_id: LocalDefId, + span: Span, +) -> ErrorGuaranteed { let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); let mut label = false; - if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) { + if let Some((def_id, visitor)) = get_owner_return_paths(tcx, opaque_def_id) { let typeck_results = tcx.typeck(def_id); if visitor .returns @@ -1431,21 +1435,30 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> E .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) { - struct OpaqueTypeCollector(Vec); + #[derive(Default)] + struct OpaqueTypeCollector { + opaques: Vec, + closures: Vec, + } impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match *t.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { - self.0.push(def); + self.opaques.push(def); ControlFlow::Continue(()) } + ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => { + self.closures.push(def_id); + t.super_visit_with(self) + } _ => t.super_visit_with(self), } } } - let mut visitor = OpaqueTypeCollector(vec![]); + + let mut visitor = OpaqueTypeCollector::default(); ty.visit_with(&mut visitor); - for def_id in visitor.0 { + for def_id in visitor.opaques { let ty_span = tcx.def_span(def_id); if !seen.contains(&ty_span) { err.span_label(ty_span, &format!("returning this opaque type `{ty}`")); @@ -1453,6 +1466,40 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> E } err.span_label(sp, &format!("returning here with type `{ty}`")); } + + for closure_def_id in visitor.closures { + let Some(closure_local_did) = closure_def_id.as_local() else { continue; }; + let typeck_results = tcx.typeck(closure_local_did); + + let mut label_match = |ty: Ty<'_>, span| { + for arg in ty.walk() { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: captured_def_id, .. }) = *ty.kind() + && captured_def_id == opaque_def_id.to_def_id() + { + err.span_label( + span, + format!( + "{} captures itself here", + tcx.def_kind(closure_def_id).descr(closure_def_id) + ), + ); + } + } + }; + + // Label any closure upvars that capture the opaque + for capture in typeck_results.closure_min_captures_flattened(closure_local_did) + { + label_match(capture.place.ty(), capture.get_path_span(tcx)); + } + // Label any generator locals that capture the opaque + for interior_ty in + typeck_results.generator_interior_types.as_ref().skip_binder() + { + label_match(interior_ty.ty, interior_ty.span); + } + } } } } diff --git a/tests/ui/impl-trait/recursive-generator.rs b/tests/ui/impl-trait/recursive-generator.rs new file mode 100644 index 0000000000000..e876f0fb43f65 --- /dev/null +++ b/tests/ui/impl-trait/recursive-generator.rs @@ -0,0 +1,23 @@ +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; + +fn foo() -> impl Generator { + //~^ ERROR cannot resolve opaque type + //~| NOTE recursive opaque type + //~| NOTE in this expansion of desugaring of + || { + //~^ NOTE returning here + let mut gen = Box::pin(foo()); + //~^ NOTE generator captures itself here + let mut r = gen.as_mut().resume(()); + while let GeneratorState::Yielded(v) = r { + yield v; + r = gen.as_mut().resume(()); + } + } +} + +fn main() { + foo(); +} diff --git a/tests/ui/impl-trait/recursive-generator.stderr b/tests/ui/impl-trait/recursive-generator.stderr new file mode 100644 index 0000000000000..e23fd4b4a85e5 --- /dev/null +++ b/tests/ui/impl-trait/recursive-generator.stderr @@ -0,0 +1,19 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-generator.rs:5:13 + | +LL | fn foo() -> impl Generator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type +... +LL | / || { +LL | | +LL | | let mut gen = Box::pin(foo()); + | | ------- generator captures itself here +LL | | +... | +LL | | } +LL | | } + | |_____- returning here with type `[generator@$DIR/recursive-generator.rs:9:5: 9:7]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 2e34d3d4275ad..ebb231ae14f0d 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -53,6 +53,7 @@ LL | fn closure_capture() -> impl Sized { ... LL | / move || { LL | | x; + | | - closure captures itself here LL | | } | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12]` @@ -64,6 +65,7 @@ LL | fn closure_ref_capture() -> impl Sized { ... LL | / move || { LL | | &x; + | | - closure captures itself here LL | | } | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12]` @@ -94,6 +96,7 @@ LL | fn generator_capture() -> impl Sized { LL | / move || { LL | | yield; LL | | x; + | | - generator captures itself here LL | | } | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12]` @@ -114,6 +117,7 @@ LL | fn generator_hold() -> impl Sized { LL | LL | / move || { LL | | let x = generator_hold(); + | | - generator captures itself here LL | | yield; LL | | x; LL | | } From 397e4b13f9441afb6e1e4d78708dd5f99aefe685 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 Jan 2023 12:15:42 +0100 Subject: [PATCH 10/14] Migrate scraped-examples top and bottom "borders" to CSS variables --- src/librustdoc/html/static/css/rustdoc.css | 6 ++++++ src/librustdoc/html/static/css/themes/ayu.css | 9 ++------- src/librustdoc/html/static/css/themes/dark.css | 9 ++------- src/librustdoc/html/static/css/themes/light.css | 9 ++------- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a08b8d89db67d..8ff8ea088be91 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1907,9 +1907,15 @@ in storage.js } .scraped-example:not(.expanded) .code-wrapper:before { top: 0; + background: linear-gradient(to bottom, + var(--scrape-example-code-wrapper-background-start), + var(--scrape-example-code-wrapper-background-end)); } .scraped-example:not(.expanded) .code-wrapper:after { bottom: 0; + background: linear-gradient(to top, + var(--scrape-example-code-wrapper-background-start), + var(--scrape-example-code-wrapper-background-end)); } .scraped-example .code-wrapper .example-wrap { diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 979e7e0f999ed..ed779bf6166ee 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -97,6 +97,8 @@ Original by Dempfi (https://github.com/dempfi/ayu) --scrape-example-help-color: #eee; --scrape-example-help-hover-border-color: #fff; --scrape-example-help-hover-color: #fff; + --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1); + --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0); } h1, h2, h3, h4 { @@ -203,10 +205,3 @@ above the `@media (max-width: 700px)` rules due to a bug in the css checker */ #source-sidebar div.files > a.selected { color: #ffb44c; } - -.scraped-example:not(.expanded) .code-wrapper::before { - background: linear-gradient(to bottom, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); -} -.scraped-example:not(.expanded) .code-wrapper::after { - background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); -} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index fb15863b027ca..3766f0daa42ff 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -92,6 +92,8 @@ --scrape-example-help-color: #eee; --scrape-example-help-hover-border-color: #fff; --scrape-example-help-hover-color: #fff; + --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1); + --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); } #search-tabs > button:not(.selected) { @@ -103,10 +105,3 @@ border-top-color: #0089ff; background-color: #353535; } - -.scraped-example:not(.expanded) .code-wrapper::before { - background: linear-gradient(to bottom, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); -} -.scraped-example:not(.expanded) .code-wrapper::after { - background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); -} diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 053fa78d1dc58..8a7f6abcf8d8e 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -89,6 +89,8 @@ --scrape-example-help-color: #333; --scrape-example-help-hover-border-color: #000; --scrape-example-help-hover-color: #000; + --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1); + --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); } #search-tabs > button:not(.selected) { @@ -100,10 +102,3 @@ background-color: #ffffff; border-top-color: #0089ff; } - -.scraped-example:not(.expanded) .code-wrapper::before { - background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); -} -.scraped-example:not(.expanded) .code-wrapper::after { - background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); -} From 372ad130d595d50a8c79c836fcadc3b39d14859c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 Jan 2023 12:16:02 +0100 Subject: [PATCH 11/14] Extend rustdoc GUI test for scraped examples top and bottom "borders" --- tests/rustdoc-gui/scrape-examples-color.goml | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/rustdoc-gui/scrape-examples-color.goml b/tests/rustdoc-gui/scrape-examples-color.goml index 40f31b2771b25..67c58826efc26 100644 --- a/tests/rustdoc-gui/scrape-examples-color.goml +++ b/tests/rustdoc-gui/scrape-examples-color.goml @@ -58,3 +58,39 @@ call-function: ("check-colors", { "help_hover_border": "rgb(0, 0, 0)", "help_hover_color": "rgb(0, 0, 0)", }) + +// Now testing the top and bottom background in case there is only one scraped examples. +goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html" + +define-function: ( + "check-background", + (theme, background_color_start, background_color_end), + block { + local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false", } + reload: + assert-css: (".scraped-example:not(.expanded) .code-wrapper::before", { + "background-image": "linear-gradient(" + |background_color_start| + ", " + + |background_color_end| + ")", + }) + assert-css: (".scraped-example:not(.expanded) .code-wrapper::after", { + "background-image": "linear-gradient(to top, " + |background_color_start| + ", " + + |background_color_end| + ")", + }) + }, +) + +call-function: ("check-background", { + "theme": "ayu", + "background_color_start": "rgb(15, 20, 25)", + "background_color_end": "rgba(15, 20, 25, 0)", +}) +call-function: ("check-background", { + "theme": "dark", + "background_color_start": "rgb(53, 53, 53)", + "background_color_end": "rgba(53, 53, 53, 0)", +}) +call-function: ("check-background", { + "theme": "light", + "background_color_start": "rgb(255, 255, 255)", + "background_color_end": "rgba(255, 255, 255, 0)", +}) From 248988923195a17b14b740a4f550d87d33477916 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 20 Jan 2023 02:05:57 +0000 Subject: [PATCH 12/14] Add compare-mode-next-solver --- src/tools/compiletest/src/common.rs | 3 +++ src/tools/compiletest/src/header.rs | 1 + src/tools/compiletest/src/runtest.rs | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index a5f5eb447da6c..3676f69b100db 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -123,6 +123,7 @@ pub enum FailMode { pub enum CompareMode { Polonius, Chalk, + NextSolver, SplitDwarf, SplitDwarfSingle, } @@ -132,6 +133,7 @@ impl CompareMode { match *self { CompareMode::Polonius => "polonius", CompareMode::Chalk => "chalk", + CompareMode::NextSolver => "next-solver", CompareMode::SplitDwarf => "split-dwarf", CompareMode::SplitDwarfSingle => "split-dwarf-single", } @@ -141,6 +143,7 @@ impl CompareMode { match s.as_str() { "polonius" => CompareMode::Polonius, "chalk" => CompareMode::Chalk, + "next-solver" => CompareMode::NextSolver, "split-dwarf" => CompareMode::SplitDwarf, "split-dwarf-single" => CompareMode::SplitDwarfSingle, x => panic!("unknown --compare-mode option: {}", x), diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index c49ecb104a74a..9d99231e4c7b9 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -701,6 +701,7 @@ impl Config { match self.compare_mode { Some(CompareMode::Polonius) => name == "compare-mode-polonius", Some(CompareMode::Chalk) => name == "compare-mode-chalk", + Some(CompareMode::NextSolver) => name == "compare-mode-next-solver", Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf", Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single", None => false, diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index a16ab11e2f978..a181cdf2da60a 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2013,6 +2013,9 @@ impl<'test> TestCx<'test> { Some(CompareMode::Chalk) => { rustc.args(&["-Ztrait-solver=chalk"]); } + Some(CompareMode::NextSolver) => { + rustc.args(&["-Ztrait-solver=next"]); + } Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => { rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]); } From d6a411c086e61926aa52022b1df3a4a94e934906 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 20 Jan 2023 03:05:06 +0000 Subject: [PATCH 13/14] Implement some more predicates --- .../rustc_trait_selection/src/solve/mod.rs | 64 +++++++++++++++++-- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 32eb84635b536..e89d3df04db0b 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -26,7 +26,9 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::Obligation; use rustc_middle::infer::canonical::Certainty as OldCertainty; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate}; +use rustc_middle::ty::{ + RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, +}; use rustc_span::DUMMY_SP; use crate::traits::ObligationCause; @@ -243,16 +245,34 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => { self.compute_region_outlives_goal(Goal { param_env, predicate }) } + ty::PredicateKind::Subtype(predicate) => { + self.compute_subtype_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Coerce(predicate) => self.compute_subtype_goal(Goal { + param_env, + predicate: SubtypePredicate { + a_is_expected: true, + a: predicate.a, + b: predicate.b, + }, + }), + ty::PredicateKind::ClosureKind(_, substs, kind) => self.compute_closure_kind_goal( + substs.as_closure().kind_ty().to_opt_closure_kind(), + kind, + ), + ty::PredicateKind::Ambiguous => { + self.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + } // FIXME: implement these predicates :) ty::PredicateKind::WellFormed(_) | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(_, _, _) - | ty::PredicateKind::Subtype(_) - | ty::PredicateKind::Coerce(_) | ty::PredicateKind::ConstEvaluatable(_) - | ty::PredicateKind::ConstEquate(_, _) - | ty::PredicateKind::TypeWellFormedFromEnv(_) - | ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::Yes), + | ty::PredicateKind::ConstEquate(_, _) => { + self.make_canonical_response(Certainty::Yes) + } + ty::PredicateKind::TypeWellFormedFromEnv(..) => { + bug!("TypeWellFormedFromEnv is only used for Chalk") + } } } else { let kind = self.infcx.replace_bound_vars_with_placeholders(kind); @@ -275,6 +295,36 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ) -> QueryResult<'tcx> { self.make_canonical_response(Certainty::Yes) } + + fn compute_subtype_goal( + &mut self, + goal: Goal<'tcx, SubtypePredicate<'tcx>>, + ) -> QueryResult<'tcx> { + self.infcx.probe(|_| { + let InferOk { value: (), obligations } = self + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .sub(goal.predicate.a, goal.predicate.b)?; + self.evaluate_all_and_make_canonical_response( + obligations.into_iter().map(|pred| pred.into()).collect(), + ) + }) + } + + fn compute_closure_kind_goal( + &mut self, + found_kind: Option, + expected_kind: ty::ClosureKind, + ) -> QueryResult<'tcx> { + let Some(found_kind) = found_kind else { + return self.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)); + }; + if found_kind.extends(expected_kind) { + self.make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } } impl<'tcx> EvalCtxt<'_, 'tcx> { From 444cbcd729e4b55602fedcaef7fe902316d8ab49 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 20 Jan 2023 18:38:33 +0000 Subject: [PATCH 14/14] Address goal nits --- .../src/solve/assembly.rs | 6 +- .../rustc_trait_selection/src/solve/mod.rs | 73 ++++++++++++------- .../src/solve/project_goals.rs | 7 +- .../src/solve/trait_goals.rs | 6 +- 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 31c1bc9ecc062..cdb72d49834f0 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -1,7 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. use super::infcx_ext::InferCtxtExt; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate_predicates; @@ -148,9 +148,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if goal.predicate.self_ty().is_ty_var() { return vec![Candidate { source: CandidateSource::BuiltinImpl, - result: self - .make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) - .unwrap(), + result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(), }]; } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index e89d3df04db0b..da2a1a19957e1 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -19,6 +19,7 @@ use std::mem; +use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues}; use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; @@ -27,7 +28,7 @@ use rustc_infer::traits::Obligation; use rustc_middle::infer::canonical::Certainty as OldCertainty; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ - RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, + CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, }; use rustc_span::DUMMY_SP; @@ -89,6 +90,8 @@ pub enum Certainty { } impl Certainty { + pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + /// When proving multiple goals using **AND**, e.g. nested obligations for an impl, /// use this function to unify the certainty of these goals pub fn unify_and(self, other: Certainty) -> Certainty { @@ -248,21 +251,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Subtype(predicate) => { self.compute_subtype_goal(Goal { param_env, predicate }) } - ty::PredicateKind::Coerce(predicate) => self.compute_subtype_goal(Goal { - param_env, - predicate: SubtypePredicate { - a_is_expected: true, - a: predicate.a, - b: predicate.b, - }, - }), - ty::PredicateKind::ClosureKind(_, substs, kind) => self.compute_closure_kind_goal( - substs.as_closure().kind_ty().to_opt_closure_kind(), - kind, - ), - ty::PredicateKind::Ambiguous => { - self.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + ty::PredicateKind::Coerce(predicate) => { + self.compute_coerce_goal(Goal { param_env, predicate }) } + ty::PredicateKind::ClosureKind(def_id, substs, kind) => self + .compute_closure_kind_goal(Goal { + param_env, + predicate: (def_id, substs, kind), + }), + ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS), // FIXME: implement these predicates :) ty::PredicateKind::WellFormed(_) | ty::PredicateKind::ObjectSafe(_) @@ -296,28 +293,50 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.make_canonical_response(Certainty::Yes) } + fn compute_coerce_goal( + &mut self, + goal: Goal<'tcx, CoercePredicate<'tcx>>, + ) -> QueryResult<'tcx> { + self.compute_subtype_goal(Goal { + param_env: goal.param_env, + predicate: SubtypePredicate { + a_is_expected: false, + a: goal.predicate.a, + b: goal.predicate.b, + }, + }) + } + fn compute_subtype_goal( &mut self, goal: Goal<'tcx, SubtypePredicate<'tcx>>, ) -> QueryResult<'tcx> { - self.infcx.probe(|_| { - let InferOk { value: (), obligations } = self - .infcx - .at(&ObligationCause::dummy(), goal.param_env) - .sub(goal.predicate.a, goal.predicate.b)?; - self.evaluate_all_and_make_canonical_response( - obligations.into_iter().map(|pred| pred.into()).collect(), - ) - }) + if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() { + // FIXME: Do we want to register a subtype relation between these vars? + // That won't actually reflect in the query response, so it seems moot. + self.make_canonical_response(Certainty::AMBIGUOUS) + } else { + self.infcx.probe(|_| { + let InferOk { value: (), obligations } = self + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .sub(goal.predicate.a, goal.predicate.b)?; + self.evaluate_all_and_make_canonical_response( + obligations.into_iter().map(|pred| pred.into()).collect(), + ) + }) + } } fn compute_closure_kind_goal( &mut self, - found_kind: Option, - expected_kind: ty::ClosureKind, + goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>, ) -> QueryResult<'tcx> { + let (_, substs, expected_kind) = goal.predicate; + let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind(); + let Some(found_kind) = found_kind else { - return self.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)); + return self.make_canonical_response(Certainty::AMBIGUOUS); }; if found_kind.extends(expected_kind) { self.make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e39fa05339286..32e15f03998b3 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -3,7 +3,7 @@ use crate::traits::{specialization_graph, translate_substs}; use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; use super::trait_goals::structural_traits; -use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use super::{Certainty, EvalCtxt, Goal, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -229,8 +229,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal.predicate.def_id(), impl_def_id )? else { - let certainty = Certainty::Maybe(MaybeCause::Ambiguity); - return ecx.make_canonical_response(trait_ref_certainty.unify_and(certainty)); + return ecx.make_canonical_response(trait_ref_certainty.unify_and(Certainty::AMBIGUOUS)); }; if !assoc_def.item.defaultness(tcx).has_value() { @@ -382,7 +381,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { .to_predicate(ecx.tcx()); Self::consider_assumption(ecx, goal, pred) } else { - ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + 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 9985d7181bb7d..4b6d673c999c9 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -4,7 +4,7 @@ use std::iter; use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; -use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use super::{Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; @@ -133,7 +133,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.self_ty().has_non_region_infer() { - return ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)); + return ecx.make_canonical_response(Certainty::AMBIGUOUS); } let tcx = ecx.tcx(); @@ -171,7 +171,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .to_predicate(ecx.tcx()); Self::consider_assumption(ecx, goal, pred) } else { - ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) + ecx.make_canonical_response(Certainty::AMBIGUOUS) } }