From 75cc2b8e8ceadca1672594a7e2056eade5da951d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 3 Jul 2023 13:26:08 +0200 Subject: [PATCH 1/5] Fix display of long inline cfg labels --- src/librustdoc/html/static/css/rustdoc.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index ccfd4cc5b875b..b7f455259ef4f 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -971,6 +971,8 @@ so that we can apply CSS-filters to change the arrow color in themes */ display: flex; padding: 3px; margin-bottom: 5px; + align-items: center; + vertical-align: text-bottom; } .item-name .stab { margin-left: 0.3125em; @@ -982,11 +984,9 @@ so that we can apply CSS-filters to change the arrow color in themes */ color: var(--main-color); background-color: var(--stab-background-color); width: fit-content; - align-items: center; white-space: pre-wrap; border-radius: 3px; - display: inline-flex; - vertical-align: text-bottom; + display: inline; } .stab.portability > code { From 9d130582ab00195d4bc07c1cc7bcbcefd46e779c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 3 Jul 2023 13:26:28 +0200 Subject: [PATCH 2/5] Add GUI test for long inline cfg labels display --- tests/rustdoc-gui/label-next-to-symbol.goml | 12 +++++++++++- tests/rustdoc-gui/src/test_docs/lib.rs | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml index 6c6380256bd1c..eb603d6c6b6a5 100644 --- a/tests/rustdoc-gui/label-next-to-symbol.goml +++ b/tests/rustdoc-gui/label-next-to-symbol.goml @@ -37,7 +37,6 @@ compare-elements-position: ( ("y"), ) - // Mobile view set-window-size: (600, 600) // staggered layout with 2em spacing @@ -64,3 +63,14 @@ compare-elements-position-false: ( "//*[@class='desc docblock-short'][text()='a thing with a label']", ("y"), ) + +// Ensure it doesn't expand. +set-window-size: (800, 800) +go-to: "file://" + |DOC_PATH| + "/test_docs/cfgs/index.html" +// This part of the tags should not be on the same line as the beginning since the width +// is too small for that. +compare-elements-position-false: ( + "//*[@class='stab portability']/code[text()='appservice-api-c']", + "//*[@class='stab portability']/code[text()='server']", + ("y"), +) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index c040fa02dffeb..ecf3a7cc147ca 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -507,3 +507,12 @@ pub mod fields { }, } } + +pub mod cfgs { + #[doc(cfg(all( + any(not(feature = "appservice-api-c"), not(feature = "appservice-api-s")), + any(not(feature = "client"), not(feature = "server")), + )))] + /// Some docs. + pub mod cfgs {} +} From 838f85d6f7f5d6c5bff1dad5ef9116c5749a122b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 3 Jul 2023 13:34:54 +0200 Subject: [PATCH 3/5] Don't perform selection if IATs are not enabled --- .../rustc_hir_analysis/src/astconv/mod.rs | 14 ++++--- .../assoc-inherent-unstable.rs | 3 ++ .../assoc-inherent-unstable.stderr | 2 +- .../bugs/cycle-iat-inside-of-adt.rs | 4 ++ .../bugs/cycle-iat-inside-of-adt.stderr | 40 ++++++++----------- .../dont-select-if-disabled.rs | 17 ++++++++ .../dont-select-if-disabled.stderr | 24 +++++++++++ .../issue-109071.no_gate.stderr | 10 ++++- .../associated-inherent-types/issue-109071.rs | 1 + tests/ui/resolve/resolve-self-in-impl.stderr | 30 +++++++------- 10 files changed, 99 insertions(+), 46 deletions(-) create mode 100644 tests/ui/associated-inherent-types/dont-select-if-disabled.rs create mode 100644 tests/ui/associated-inherent-types/dont-select-if-disabled.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 2f5bcf8d64782..9b5ff3240ed62 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1893,6 +1893,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) -> Result, DefId)>, ErrorGuaranteed> { let tcx = self.tcx(); + // Don't attempt to look up inherent associated types when the feature is not enabled. + // Theoretically it'd be fine to do so since we feature-gate their definition site. + // However, due to current limitations of the implementation (caused by us performing + // selection in AstConv), IATs can lead to cycle errors (#108491, #110106) which mask the + // feature-gate error, needlessly confusing users that use IATs by accident (#113265). + if !tcx.features().inherent_associated_types { + return Ok(None); + } + let candidates: Vec<_> = tcx .inherent_impls(adt_did) .iter() @@ -1903,11 +1912,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } - if !tcx.features().inherent_associated_types { - tcx.sess - .delay_span_bug(span, "found inherent assoc type without the feature being gated"); - } - // // Select applicable inherent associated type candidates modulo regions. // diff --git a/tests/ui/associated-inherent-types/assoc-inherent-unstable.rs b/tests/ui/associated-inherent-types/assoc-inherent-unstable.rs index 34b4e47bf462e..152bb7a60a704 100644 --- a/tests/ui/associated-inherent-types/assoc-inherent-unstable.rs +++ b/tests/ui/associated-inherent-types/assoc-inherent-unstable.rs @@ -1,6 +1,9 @@ // aux-crate:aux=assoc-inherent-unstable.rs // edition: 2021 +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + type Data = aux::Owner::Data; //~ ERROR use of unstable library feature 'data' fn main() {} diff --git a/tests/ui/associated-inherent-types/assoc-inherent-unstable.stderr b/tests/ui/associated-inherent-types/assoc-inherent-unstable.stderr index c0be8bfd79bfc..415ee0193c94c 100644 --- a/tests/ui/associated-inherent-types/assoc-inherent-unstable.stderr +++ b/tests/ui/associated-inherent-types/assoc-inherent-unstable.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'data' - --> $DIR/assoc-inherent-unstable.rs:4:13 + --> $DIR/assoc-inherent-unstable.rs:7:13 | LL | type Data = aux::Owner::Data; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs index f41574403d887..33c73c3db897b 100644 --- a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs @@ -1,5 +1,7 @@ // known-bug: #108491 +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] // FIXME(inherent_associated_types): This should pass. struct Foo { @@ -8,3 +10,5 @@ struct Foo { impl Foo { pub type Bar = usize; } + +fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr index f313c4946714c..23269e1afab54 100644 --- a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr @@ -1,49 +1,43 @@ -error[E0601]: `main` function not found in crate `cycle_iat_inside_of_adt` - --> $DIR/cycle-iat-inside-of-adt.rs:10:2 - | -LL | } - | ^ consider adding a `main` function to `$DIR/cycle-iat-inside-of-adt.rs` - error[E0391]: cycle detected when computing predicates of `Foo` - --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + --> $DIR/cycle-iat-inside-of-adt.rs:7:1 | LL | struct Foo { | ^^^^^^^^^^ | note: ...which requires computing predicates of `Foo`... - --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + --> $DIR/cycle-iat-inside-of-adt.rs:7:1 | LL | struct Foo { | ^^^^^^^^^^ note: ...which requires computing inferred outlives predicates of `Foo`... - --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + --> $DIR/cycle-iat-inside-of-adt.rs:7:1 | LL | struct Foo { | ^^^^^^^^^^ = note: ...which requires computing the inferred outlives predicates for items in this crate... note: ...which requires computing type of `Foo::bar`... - --> $DIR/cycle-iat-inside-of-adt.rs:6:5 + --> $DIR/cycle-iat-inside-of-adt.rs:8:5 | LL | bar: Self::Bar, | ^^^^^^^^^^^^^^ note: ...which requires computing normalized predicates of `Foo`... - --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + --> $DIR/cycle-iat-inside-of-adt.rs:7:1 | LL | struct Foo { | ^^^^^^^^^^ = note: ...which again requires computing predicates of `Foo`, completing the cycle note: cycle used when collecting item types in top-level module - --> $DIR/cycle-iat-inside-of-adt.rs:5:1 - | -LL | / struct Foo { -LL | | bar: Self::Bar, -LL | | } -LL | | impl Foo { -LL | | pub type Bar = usize; -LL | | } - | |_^ + --> $DIR/cycle-iat-inside-of-adt.rs:3:1 + | +LL | / #![feature(inherent_associated_types)] +LL | | #![allow(incomplete_features)] +LL | | // FIXME(inherent_associated_types): This should pass. +LL | | +... | +LL | | +LL | | fn main() {} + | |____________^ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0391, E0601. -For more information about an error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/associated-inherent-types/dont-select-if-disabled.rs b/tests/ui/associated-inherent-types/dont-select-if-disabled.rs new file mode 100644 index 0000000000000..472be4fbfbcbf --- /dev/null +++ b/tests/ui/associated-inherent-types/dont-select-if-disabled.rs @@ -0,0 +1,17 @@ +// Regression test for #113265. + +// Don't perform selection if the feature is not enabled to prevent cycle errors +// that exist due to current limitations of the implementation from masking the +// feature-gate error. See the aforementioned issue. +// This does lead to rustc not mentioning inherent associated types at usage-sites of +// IATs that were defined in an external crate but that's acceptable for now. + +// FIXME(inherent_associated_types): Revisit this decision once the implementation is smarter. + +// The following program would currently lead to a cycle if IATs were enabled. + +struct S(S::P); //~ ERROR ambiguous associated type + +impl S { type P = (); } //~ ERROR inherent associated types are unstable + +fn main() {} diff --git a/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr b/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr new file mode 100644 index 0000000000000..87a3f35c9685a --- /dev/null +++ b/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr @@ -0,0 +1,24 @@ +error[E0223]: ambiguous associated type + --> $DIR/dont-select-if-disabled.rs:13:10 + | +LL | struct S(S::P); + | ^^^^ + | +help: if there were a trait named `Example` with associated type `P` implemented for `S`, you could use the fully-qualified path + | +LL | struct S(::P); + | ~~~~~~~~~~~~~~~~~ + +error[E0658]: inherent associated types are unstable + --> $DIR/dont-select-if-disabled.rs:15:10 + | +LL | impl S { type P = (); } + | ^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0223, E0658. +For more information about an error, try `rustc --explain E0223`. diff --git a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr index 8b6a8206569f6..6f206f2b89c79 100644 --- a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr @@ -29,7 +29,13 @@ LL | type Item = &[T]; = note: see issue #8995 for more information = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable -error: aborting due to 3 previous errors +error[E0223]: ambiguous associated type + --> $DIR/issue-109071.rs:15:22 + | +LL | fn T() -> Option {} + | ^^^^^^^^^^ help: use the fully-qualified path: ` as IntoIterator>::Item` + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0107, E0637, E0658. +Some errors have detailed explanations: E0107, E0223, E0637, E0658. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-inherent-types/issue-109071.rs b/tests/ui/associated-inherent-types/issue-109071.rs index 73b969d5940ab..951c708e3f95b 100644 --- a/tests/ui/associated-inherent-types/issue-109071.rs +++ b/tests/ui/associated-inherent-types/issue-109071.rs @@ -13,6 +13,7 @@ impl Windows { //~ ERROR: missing generics for struct `Windows` impl Windows { fn T() -> Option {} + //[no_gate]~^ ERROR: ambiguous associated type } fn main() {} diff --git a/tests/ui/resolve/resolve-self-in-impl.stderr b/tests/ui/resolve/resolve-self-in-impl.stderr index b3042d413468a..9f9ed68898f6c 100644 --- a/tests/ui/resolve/resolve-self-in-impl.stderr +++ b/tests/ui/resolve/resolve-self-in-impl.stderr @@ -1,40 +1,40 @@ error: `Self` is not valid in the self type of an impl block - --> $DIR/resolve-self-in-impl.rs:16:6 + --> $DIR/resolve-self-in-impl.rs:14:13 | -LL | impl Self {} - | ^^^^ +LL | impl Tr for Self {} + | ^^^^ | = note: replace `Self` with a different type error: `Self` is not valid in the self type of an impl block - --> $DIR/resolve-self-in-impl.rs:17:8 + --> $DIR/resolve-self-in-impl.rs:15:15 | -LL | impl S {} - | ^^^^ +LL | impl Tr for S {} + | ^^^^ | = note: replace `Self` with a different type error: `Self` is not valid in the self type of an impl block - --> $DIR/resolve-self-in-impl.rs:18:7 + --> $DIR/resolve-self-in-impl.rs:16:6 | -LL | impl (Self, Self) {} - | ^^^^ ^^^^ +LL | impl Self {} + | ^^^^ | = note: replace `Self` with a different type error: `Self` is not valid in the self type of an impl block - --> $DIR/resolve-self-in-impl.rs:14:13 + --> $DIR/resolve-self-in-impl.rs:17:8 | -LL | impl Tr for Self {} - | ^^^^ +LL | impl S {} + | ^^^^ | = note: replace `Self` with a different type error: `Self` is not valid in the self type of an impl block - --> $DIR/resolve-self-in-impl.rs:15:15 + --> $DIR/resolve-self-in-impl.rs:18:7 | -LL | impl Tr for S {} - | ^^^^ +LL | impl (Self, Self) {} + | ^^^^ ^^^^ | = note: replace `Self` with a different type From 298c0d1a6201630d9a9e741d3c9e41e96acafb6d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 21 Jun 2023 01:22:43 +0000 Subject: [PATCH 4/5] Implement selection in new trait solver --- .../src/solve/assembly/mod.rs | 51 ++- .../src/solve/eval_ctxt.rs | 2 + .../src/solve/eval_ctxt/canonical.rs | 8 +- .../src/solve/eval_ctxt/select.rs | 303 ++++++++++++++++++ .../rustc_trait_selection/src/solve/mod.rs | 2 +- .../src/traits/select/mod.rs | 7 +- tests/ui/for/issue-20605.next.stderr | 4 + 7 files changed, 356 insertions(+), 21 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 9e8dbd0cde217..15e08166a8b87 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -49,7 +49,7 @@ pub(super) enum CandidateSource { /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`. /// For a list of all traits with builtin impls, check out the /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not - BuiltinImpl, + BuiltinImpl(BuiltinImplSource), /// An assumption from the environment. /// /// More precisely we've used the `n-th` assumption in the `param_env`. @@ -87,6 +87,16 @@ pub(super) enum CandidateSource { AliasBound, } +/// Records additional information about what kind of built-in impl this is. +/// This should only be used by selection. +#[derive(Debug, Clone, Copy)] +pub(super) enum BuiltinImplSource { + TraitUpcasting, + Object, + Misc, + Ambiguity, +} + /// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq + std::fmt::Display @@ -295,7 +305,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // least structurally resolve the type one layer. if goal.predicate.self_ty().is_ty_var() { return vec![Candidate { - source: CandidateSource::BuiltinImpl, + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), result: self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .unwrap(), @@ -344,7 +354,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let result = ecx.evaluate_added_goals_and_make_canonical_response( Certainty::Maybe(MaybeCause::Overflow), )?; - Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }]) + Ok(vec![Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + result, + }]) }, |ecx| { let normalized_ty = ecx.next_ty_infer(); @@ -447,9 +460,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; match result { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) - } + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), Err(NoSolution) => (), } @@ -457,7 +471,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` if lang_items.unsize_trait() == Some(trait_def_id) { for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }); + candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), + result, + }); } } } @@ -621,9 +638,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; match result { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) - } + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), Err(NoSolution) => (), } } @@ -688,9 +706,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } match G::consider_object_bound_candidate(self, goal, assumption) { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) - } + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + result, + }), Err(NoSolution) => (), } } @@ -711,8 +730,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(_) => match self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { - Ok(result) => candidates - .push(Candidate { source: CandidateSource::BuiltinImpl, result }), + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + result, + }), // FIXME: This will be reachable at some point if we're in // `assemble_candidates_after_normalizing_self_ty` and we get a // universe error. We'll deal with it at this point. diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 6aca40b8dbd98..0db2ecb517a56 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -28,9 +28,11 @@ use super::inspect::ProofTreeBuilder; use super::search_graph::{self, OverflowHandler}; use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; +pub use select::InferCtxtSelectExt; mod canonical; mod probe; +mod select; pub struct EvalCtxt<'a, 'tcx> { /// The inference context that backs (mostly) inference and placeholder terms diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 851edf1fa1cb6..637d458882c16 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -20,7 +20,7 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, }; -use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty}; +use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -28,10 +28,10 @@ use std::ops::Deref; impl<'tcx> EvalCtxt<'_, 'tcx> { /// Canonicalizes the goal remembering the original values /// for each bound variable. - pub(super) fn canonicalize_goal( + pub(super) fn canonicalize_goal>>( &self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> (Vec>, CanonicalInput<'tcx>) { + goal: Goal<'tcx, T>, + ) -> (Vec>, CanonicalInput<'tcx, T>) { let mut orig_values = Default::default(); let canonical_goal = Canonicalizer::canonicalize( self.infcx, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs new file mode 100644 index 0000000000000..7680df13802e0 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -0,0 +1,303 @@ +use std::ops::ControlFlow; + +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::util::supertraits; +use rustc_infer::traits::{ + Obligation, PredicateObligation, Selection, SelectionResult, TraitObligation, +}; +use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::traits::solve::{Certainty, Goal, PredefinedOpaquesData, QueryInput}; +use rustc_middle::traits::{ + DefiningAnchor, ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, + ImplSourceUserDefinedData, ObligationCause, SelectionError, +}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::DUMMY_SP; + +use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; +use crate::solve::eval_ctxt::{EvalCtxt, NestedGoals}; +use crate::solve::inspect::ProofTreeBuilder; +use crate::solve::search_graph::SearchGraph; +use crate::solve::SolverMode; +use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; + +pub trait InferCtxtSelectExt<'tcx> { + fn select_in_new_trait_solver( + &self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>>; +} + +impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { + fn select_in_new_trait_solver( + &self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>> { + assert!(self.next_trait_solver()); + + let goal = Goal::new( + self.tcx, + obligation.param_env, + self.instantiate_binder_with_placeholders(obligation.predicate), + ); + + let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; + let mut search_graph = SearchGraph::new(self.tcx, mode); + let mut ecx = EvalCtxt { + search_graph: &mut search_graph, + infcx: self, + // Only relevant when canonicalizing the response, + // which we don't do within this evaluation context. + predefined_opaques_in_body: self + .tcx + .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), + // Only relevant when canonicalizing the response. + max_input_universe: ty::UniverseIndex::ROOT, + var_values: CanonicalVarValues::dummy(), + nested_goals: NestedGoals::new(), + tainted: Ok(()), + inspect: ProofTreeBuilder::new_noop(), + }; + + let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal); + let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal); + + // pseudo-winnow + if candidates.len() == 0 { + return Err(SelectionError::Unimplemented); + } else if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { + candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]) + }); + if should_drop_i { + candidates.swap_remove(i); + } else { + i += 1; + if i > 1 { + return Ok(None); + } + } + } + } + + let candidate = candidates.pop().unwrap(); + let (certainty, nested_goals) = ecx + .instantiate_and_apply_query_response(goal.param_env, orig_values, candidate.result) + .map_err(|_| SelectionError::Unimplemented)?; + + let goal = self.resolve_vars_if_possible(goal); + + let nested_obligations: Vec<_> = nested_goals + .into_iter() + .map(|goal| { + Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate) + }) + .collect(); + + if let Certainty::Maybe(_) = certainty { + return Ok(None); + } + + match (certainty, candidate.source) { + (_, CandidateSource::Impl(def_id)) => { + rematch_impl(self, goal, def_id, nested_obligations) + } + + ( + _, + CandidateSource::BuiltinImpl( + BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting, + ), + ) => rematch_object(self, goal, nested_obligations), + + (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { + // technically some builtin impls have nested obligations, but if + // `Certainty::Yes`, then they should've all been verified by the + // evaluation above. + Ok(Some(ImplSource::Builtin(nested_obligations))) + } + + (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { + // It's fine not to do anything to rematch these, since there are no + // nested obligations. + Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) + } + + (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity)) + | (Certainty::Maybe(_), _) => Ok(None), + } + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + fn compute_canonical_trait_candidates( + &mut self, + canonical_input: Canonical<'tcx, QueryInput<'tcx, ty::TraitPredicate<'tcx>>>, + ) -> Vec> { + let intercrate = match self.search_graph.solver_mode() { + SolverMode::Normal => false, + SolverMode::Coherence => true, + }; + let (canonical_infcx, input, var_values) = self + .tcx() + .infer_ctxt() + .intercrate(intercrate) + .with_next_trait_solver(true) + .with_opaque_type_inference(canonical_input.value.anchor) + .build_with_canonical(DUMMY_SP, &canonical_input); + + let mut ecx = EvalCtxt { + infcx: &canonical_infcx, + var_values, + predefined_opaques_in_body: input.predefined_opaques_in_body, + max_input_universe: canonical_input.max_universe, + search_graph: &mut self.search_graph, + nested_goals: NestedGoals::new(), + tainted: Ok(()), + inspect: ProofTreeBuilder::new_noop(), + }; + + for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { + ecx.insert_hidden_type(key, input.goal.param_env, ty) + .expect("failed to prepopulate opaque types"); + } + + let candidates = ecx.assemble_and_evaluate_candidates(input.goal); + + // We don't need the canonicalized context anymore + if input.anchor != DefiningAnchor::Error { + let _ = canonical_infcx.take_opaque_types(); + } + + candidates + } +} + +fn candidate_should_be_dropped_in_favor_of<'tcx>( + victim: &Candidate<'tcx>, + other: &Candidate<'tcx>, +) -> bool { + match (victim.source, other.source) { + (CandidateSource::ParamEnv(i), CandidateSource::ParamEnv(j)) => i >= j, + (_, CandidateSource::ParamEnv(_)) => true, + _ => false, + } +} + +fn rematch_impl<'tcx>( + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, + impl_def_id: DefId, + mut nested: Vec>, +) -> SelectionResult<'tcx, Selection<'tcx>> { + let substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().subst(infcx.tcx, substs); + + nested.extend( + infcx + .at(&ObligationCause::dummy(), goal.param_env) + .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + + nested.extend( + infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, substs).into_iter().map( + |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred), + ), + ); + + Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, substs, nested }))) +} + +fn rematch_object<'tcx>( + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, + mut nested: Vec>, +) -> SelectionResult<'tcx, Selection<'tcx>> { + let self_ty = goal.predicate.self_ty(); + let source_trait_ref = if let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() { + data.principal().unwrap().with_self_ty(infcx.tcx, self_ty) + } else { + bug!() + }; + + let (is_upcasting, target_trait_ref_unnormalized) = if Some(goal.predicate.def_id()) + == infcx.tcx.lang_items().unsize_trait() + { + if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() { + (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty)) + } else { + bug!() + } + } else { + (false, ty::Binder::dummy(goal.predicate.trait_ref)) + }; + + let mut target_trait_ref = None; + for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) { + let result = infcx.commit_if_ok(|_| { + infcx.at(&ObligationCause::dummy(), goal.param_env).eq( + DefineOpaqueTypes::No, + target_trait_ref_unnormalized, + candidate_trait_ref, + ) + + // FIXME: We probably should at least shallowly verify these... + }); + + match result { + Ok(InferOk { value: (), obligations }) => { + target_trait_ref = Some(candidate_trait_ref); + nested.extend(obligations); + break; + } + Err(_) => continue, + } + } + + let target_trait_ref = target_trait_ref.unwrap(); + + let mut offset = 0; + let Some((vtable_base, vtable_vptr_slot)) = + prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| { + match segment { + VtblSegment::MetadataDSA => { + offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref); + + if trait_ref == target_trait_ref { + if emit_vptr { + return ControlFlow::Break(( + offset, + Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)), + )); + } else { + return ControlFlow::Break((offset, None)); + } + } + + offset += own_vtable_entries; + if emit_vptr { + offset += 1; + } + } + } + ControlFlow::Continue(()) + }) + else { + bug!(); + }; + + // If we're upcasting, get the offset of the vtable pointer, which is + Ok(Some(if is_upcasting { + ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + } else { + ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) + })) +} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 769d002fcc0cc..f3f78cdf09d6e 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -33,7 +33,7 @@ mod search_graph; mod trait_goals; mod weak_types; -pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt}; +pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt, InferCtxtSelectExt}; pub use fulfill::FulfillmentCtxt; pub(crate) use normalize::deeply_normalize; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7cf8479b80392..c12d92b90124c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -20,6 +20,7 @@ use super::{ }; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::solve::InferCtxtSelectExt; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::try_normalize_with_depth_to; use crate::traits::project::ProjectAndUnifyResult; @@ -264,6 +265,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { + if self.infcx.next_trait_solver() { + return self.infcx.select_in_new_trait_solver(obligation); + } + let candidate = match self.select_from_obligation(obligation) { Err(SelectionError::Overflow(OverflowError::Canonical)) => { // In standard mode, overflow must have been caught and reported @@ -290,7 +295,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - pub(crate) fn select_from_obligation( + fn select_from_obligation( &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { diff --git a/tests/ui/for/issue-20605.next.stderr b/tests/ui/for/issue-20605.next.stderr index d55efedfcbeef..de60555ae3c08 100644 --- a/tests/ui/for/issue-20605.next.stderr +++ b/tests/ui/for/issue-20605.next.stderr @@ -13,6 +13,10 @@ LL | for item in *things { *item = 0 } = help: the trait `Sized` is not implemented for ` as IntoIterator>::IntoIter` = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature +help: consider further restricting the associated type + | +LL | fn changer<'a>(mut things: Box>) where as IntoIterator>::IntoIter: Sized { + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: the type ` as IntoIterator>::IntoIter` is not well-formed --> $DIR/issue-20605.rs:5:17 From f3f879326895658ca8b07726b8ba3eddab5b7c81 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 29 Jun 2023 19:06:27 +0000 Subject: [PATCH 5/5] Helpers for creating EvalCtxts, some comments --- .../src/solve/eval_ctxt.rs | 150 +++++++++------ .../src/solve/eval_ctxt/select.rs | 177 ++++++++---------- tests/ui/for/issue-20605.next.stderr | 4 - 3 files changed, 174 insertions(+), 157 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 0db2ecb517a56..3b0c5849099a1 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -142,15 +142,34 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { Result<(bool, Certainty, Vec>>), NoSolution>, Option>, ) { - let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; - let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode); + EvalCtxt::enter_root(self, generate_proof_tree, |ecx| { + ecx.evaluate_goal(IsNormalizesToHack::No, goal) + }) + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(super) fn solver_mode(&self) -> SolverMode { + self.search_graph.solver_mode() + } + + /// Creates a root evaluation context and search graph. This should only be + /// used from outside of any evaluation, and other methods should be preferred + /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). + fn enter_root( + infcx: &InferCtxt<'tcx>, + generate_proof_tree: GenerateProofTree, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R, + ) -> (R, Option>) { + let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; + let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode); let mut ecx = EvalCtxt { search_graph: &mut search_graph, - infcx: self, + infcx: infcx, // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. - predefined_opaques_in_body: self + predefined_opaques_in_body: infcx .tcx .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), // Only relevant when canonicalizing the response. @@ -158,12 +177,12 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { var_values: CanonicalVarValues::dummy(), nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: (self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree + inspect: (infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree || matches!(generate_proof_tree, GenerateProofTree::Yes)) .then(ProofTreeBuilder::new_root) .unwrap_or_else(ProofTreeBuilder::new_noop), }; - let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal); + let result = f(&mut ecx); let tree = ecx.inspect.finalize(); if let Some(tree) = &tree { @@ -179,11 +198,66 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { assert!(search_graph.is_empty()); (result, tree) } -} -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { - pub(super) fn solver_mode(&self) -> SolverMode { - self.search_graph.solver_mode() + /// Creates a nested evaluation context that shares the same search graph as the + /// one passed in. This is suitable for evaluation, granted that the search graph + /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]), + /// but it's preferable to use other methods that call this one rather than this + /// method directly. + /// + /// This function takes care of setting up the inference context, setting the anchor, + /// and registering opaques from the canonicalized input. + fn enter_canonical( + tcx: TyCtxt<'tcx>, + search_graph: &'a mut search_graph::SearchGraph<'tcx>, + canonical_input: CanonicalInput<'tcx>, + goal_evaluation: &mut ProofTreeBuilder<'tcx>, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R, + ) -> R { + let intercrate = match search_graph.solver_mode() { + SolverMode::Normal => false, + SolverMode::Coherence => true, + }; + let (ref infcx, input, var_values) = tcx + .infer_ctxt() + .intercrate(intercrate) + .with_next_trait_solver(true) + .with_opaque_type_inference(canonical_input.value.anchor) + .build_with_canonical(DUMMY_SP, &canonical_input); + + let mut ecx = EvalCtxt { + infcx, + var_values, + predefined_opaques_in_body: input.predefined_opaques_in_body, + max_input_universe: canonical_input.max_universe, + search_graph, + nested_goals: NestedGoals::new(), + tainted: Ok(()), + inspect: goal_evaluation.new_goal_evaluation_step(input), + }; + + for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { + ecx.insert_hidden_type(key, input.goal.param_env, ty) + .expect("failed to prepopulate opaque types"); + } + + if !ecx.nested_goals.is_empty() { + panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals); + } + + let result = f(&mut ecx, input.goal); + + goal_evaluation.goal_evaluation_step(ecx.inspect); + + // When creating a query response we clone the opaque type constraints + // instead of taking them. This would cause an ICE here, since we have + // assertions against dropping an `InferCtxt` without taking opaques. + // FIXME: Once we remove support for the old impl we can remove this. + if input.anchor != DefiningAnchor::Error { + let _ = infcx.take_opaque_types(); + } + + result } /// The entry point of the solver. @@ -212,53 +286,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { canonical_input, goal_evaluation, |search_graph, goal_evaluation| { - let intercrate = match search_graph.solver_mode() { - SolverMode::Normal => false, - SolverMode::Coherence => true, - }; - let (ref infcx, input, var_values) = tcx - .infer_ctxt() - .intercrate(intercrate) - .with_next_trait_solver(true) - .with_opaque_type_inference(canonical_input.value.anchor) - .build_with_canonical(DUMMY_SP, &canonical_input); - - let mut ecx = EvalCtxt { - infcx, - var_values, - predefined_opaques_in_body: input.predefined_opaques_in_body, - max_input_universe: canonical_input.max_universe, + EvalCtxt::enter_canonical( + tcx, search_graph, - nested_goals: NestedGoals::new(), - tainted: Ok(()), - inspect: goal_evaluation.new_goal_evaluation_step(input), - }; - - for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { - ecx.insert_hidden_type(key, input.goal.param_env, ty) - .expect("failed to prepopulate opaque types"); - } - - if !ecx.nested_goals.is_empty() { - panic!( - "prepopulating opaque types shouldn't add goals: {:?}", - ecx.nested_goals - ); - } - - let result = ecx.compute_goal(input.goal); - ecx.inspect.query_result(result); - goal_evaluation.goal_evaluation_step(ecx.inspect); - - // When creating a query response we clone the opaque type constraints - // instead of taking them. This would cause an ICE here, since we have - // assertions against dropping an `InferCtxt` without taking opaques. - // FIXME: Once we remove support for the old impl we can remove this. - if input.anchor != DefiningAnchor::Error { - let _ = infcx.take_opaque_types(); - } - - result + canonical_input, + goal_evaluation, + |ecx, goal| { + let result = ecx.compute_goal(goal); + ecx.inspect.query_result(result); + result + }, + ) }, ) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 7680df13802e0..466a03e21639c 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -1,25 +1,23 @@ use std::ops::ControlFlow; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_infer::traits::util::supertraits; use rustc_infer::traits::{ Obligation, PredicateObligation, Selection, SelectionResult, TraitObligation, }; -use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_middle::traits::solve::{Certainty, Goal, PredefinedOpaquesData, QueryInput}; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; use rustc_middle::traits::{ - DefiningAnchor, ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, - ImplSourceUserDefinedData, ObligationCause, SelectionError, + ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, + ObligationCause, SelectionError, }; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::DUMMY_SP; use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; -use crate::solve::eval_ctxt::{EvalCtxt, NestedGoals}; +use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; -use crate::solve::search_graph::SearchGraph; -use crate::solve::SolverMode; +use crate::solve::search_graph::OverflowHandler; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; pub trait InferCtxtSelectExt<'tcx> { @@ -36,59 +34,54 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { ) -> SelectionResult<'tcx, Selection<'tcx>> { assert!(self.next_trait_solver()); - let goal = Goal::new( + let trait_goal = Goal::new( self.tcx, obligation.param_env, self.instantiate_binder_with_placeholders(obligation.predicate), ); - let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; - let mut search_graph = SearchGraph::new(self.tcx, mode); - let mut ecx = EvalCtxt { - search_graph: &mut search_graph, - infcx: self, - // Only relevant when canonicalizing the response, - // which we don't do within this evaluation context. - predefined_opaques_in_body: self - .tcx - .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), - // Only relevant when canonicalizing the response. - max_input_universe: ty::UniverseIndex::ROOT, - var_values: CanonicalVarValues::dummy(), - nested_goals: NestedGoals::new(), - tainted: Ok(()), - inspect: ProofTreeBuilder::new_noop(), - }; - - let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal); - let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal); - - // pseudo-winnow - if candidates.len() == 0 { - return Err(SelectionError::Unimplemented); - } else if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { - candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]) - }); - if should_drop_i { - candidates.swap_remove(i); - } else { - i += 1; - if i > 1 { - return Ok(None); + let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| { + let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate); + let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal); + let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal); + + // pseudo-winnow + if candidates.len() == 0 { + return Err(SelectionError::Unimplemented); + } else if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { + candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]) + }); + if should_drop_i { + candidates.swap_remove(i); + } else { + i += 1; + if i > 1 { + return Ok(None); + } } } } - } - let candidate = candidates.pop().unwrap(); - let (certainty, nested_goals) = ecx - .instantiate_and_apply_query_response(goal.param_env, orig_values, candidate.result) - .map_err(|_| SelectionError::Unimplemented)?; + let candidate = candidates.pop().unwrap(); + let (certainty, nested_goals) = ecx + .instantiate_and_apply_query_response( + trait_goal.param_env, + orig_values, + candidate.result, + ) + .map_err(|_| SelectionError::Unimplemented)?; - let goal = self.resolve_vars_if_possible(goal); + Ok(Some((candidate, certainty, nested_goals))) + }); + + let (candidate, certainty, nested_goals) = match result { + Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals), + Ok(None) => return Ok(None), + Err(e) => return Err(e), + }; let nested_obligations: Vec<_> = nested_goals .into_iter() @@ -97,15 +90,18 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { }) .collect(); - if let Certainty::Maybe(_) = certainty { - return Ok(None); - } - + let goal = self.resolve_vars_if_possible(trait_goal); match (certainty, candidate.source) { + // Rematching the implementation will instantiate the same nested goals that + // would have caused the ambiguity, so we can still make progress here regardless. (_, CandidateSource::Impl(def_id)) => { rematch_impl(self, goal, def_id, nested_obligations) } + // Rematching the dyn upcast or object goal will instantiate the same nested + // goals that would have caused the ambiguity, so we can still make progress here + // regardless. + // FIXME: This doesn't actually check the object bounds hold here. ( _, CandidateSource::BuiltinImpl( @@ -113,16 +109,16 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { ), ) => rematch_object(self, goal, nested_obligations), + // Technically some builtin impls have nested obligations, but if + // `Certainty::Yes`, then they should've all been verified and don't + // need re-checking. (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { - // technically some builtin impls have nested obligations, but if - // `Certainty::Yes`, then they should've all been verified by the - // evaluation above. Ok(Some(ImplSource::Builtin(nested_obligations))) } + // It's fine not to do anything to rematch these, since there are no + // nested obligations. (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { - // It's fine not to do anything to rematch these, since there are no - // nested obligations. Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) } @@ -135,44 +131,31 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> { fn compute_canonical_trait_candidates( &mut self, - canonical_input: Canonical<'tcx, QueryInput<'tcx, ty::TraitPredicate<'tcx>>>, + canonical_input: CanonicalInput<'tcx>, ) -> Vec> { - let intercrate = match self.search_graph.solver_mode() { - SolverMode::Normal => false, - SolverMode::Coherence => true, - }; - let (canonical_infcx, input, var_values) = self - .tcx() - .infer_ctxt() - .intercrate(intercrate) - .with_next_trait_solver(true) - .with_opaque_type_inference(canonical_input.value.anchor) - .build_with_canonical(DUMMY_SP, &canonical_input); - - let mut ecx = EvalCtxt { - infcx: &canonical_infcx, - var_values, - predefined_opaques_in_body: input.predefined_opaques_in_body, - max_input_universe: canonical_input.max_universe, - search_graph: &mut self.search_graph, - nested_goals: NestedGoals::new(), - tainted: Ok(()), - inspect: ProofTreeBuilder::new_noop(), - }; - - for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { - ecx.insert_hidden_type(key, input.goal.param_env, ty) - .expect("failed to prepopulate opaque types"); - } - - let candidates = ecx.assemble_and_evaluate_candidates(input.goal); - - // We don't need the canonicalized context anymore - if input.anchor != DefiningAnchor::Error { - let _ = canonical_infcx.take_opaque_types(); - } - - candidates + // This doesn't record the canonical goal on the stack during the + // candidate assembly step, but that's fine. Selection is conceptually + // outside of the solver, and if there were any cycles, we'd encounter + // the cycle anyways one step later. + EvalCtxt::enter_canonical( + self.tcx(), + self.search_graph(), + canonical_input, + // FIXME: This is wrong, idk if we even want to track stuff here. + &mut ProofTreeBuilder::new_noop(), + |ecx, goal| { + let trait_goal = Goal { + param_env: goal.param_env, + predicate: goal + .predicate + .to_opt_poly_trait_pred() + .expect("we canonicalized a trait goal") + .no_bound_vars() + .expect("we instantiated all bound vars"), + }; + ecx.assemble_and_evaluate_candidates(trait_goal) + }, + ) } } diff --git a/tests/ui/for/issue-20605.next.stderr b/tests/ui/for/issue-20605.next.stderr index de60555ae3c08..d55efedfcbeef 100644 --- a/tests/ui/for/issue-20605.next.stderr +++ b/tests/ui/for/issue-20605.next.stderr @@ -13,10 +13,6 @@ LL | for item in *things { *item = 0 } = help: the trait `Sized` is not implemented for ` as IntoIterator>::IntoIter` = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature -help: consider further restricting the associated type - | -LL | fn changer<'a>(mut things: Box>) where as IntoIterator>::IntoIter: Sized { - | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: the type ` as IntoIterator>::IntoIter` is not well-formed --> $DIR/issue-20605.rs:5:17