From d9eb5232b6fea1d902650727f71b4c1bbf69079a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 1 May 2024 17:22:39 -0400 Subject: [PATCH] Use ObligationCtxt in favor of TraitEngine in many places --- compiler/rustc_hir_analysis/src/autoderef.rs | 36 ++++----- compiler/rustc_hir_typeck/src/coercion.rs | 22 +++--- compiler/rustc_infer/src/traits/mod.rs | 12 +++ compiler/rustc_trait_selection/src/infer.rs | 11 +-- .../src/solve/inspect/analyse.rs | 18 ++--- .../src/traits/coherence.rs | 79 +++++++++---------- .../src/traits/engine.rs | 30 +++++++ .../src/traits/query/evaluate_obligation.rs | 34 ++++---- compiler/rustc_traits/src/codegen.rs | 13 ++- 9 files changed, 142 insertions(+), 113 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index f2ceb470264..4e85045ef49 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -1,6 +1,6 @@ use crate::errors::AutoDerefReachedRecursionLimit; +use crate::traits; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{self, TraitEngine, TraitEngineExt}; use rustc_infer::infer::InferCtxt; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -8,7 +8,7 @@ use rustc_session::Limit; use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Span; -use rustc_trait_selection::traits::StructurallyNormalizeExt; +use rustc_trait_selection::traits::ObligationCtxt; #[derive(Copy, Clone, Debug)] pub enum AutoderefKind { @@ -167,25 +167,19 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, Vec>)> { - let mut fulfill_cx = >::new(self.infcx); - - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let normalized_ty = match self - .infcx - .at(&cause, self.param_env) - .structurally_normalize(ty, &mut *fulfill_cx) - { - Ok(normalized_ty) => normalized_ty, - Err(errors) => { - // This shouldn't happen, except for evaluate/fulfill mismatches, - // but that's not a reason for an ICE (`predicate_may_hold` is conservative - // by design). - debug!(?errors, "encountered errors while fulfilling"); - return None; - } + let ocx = ObligationCtxt::new(self.infcx); + let Ok(normalized_ty) = ocx.structurally_normalize( + &traits::ObligationCause::misc(self.span, self.body_id), + self.param_env, + ty, + ) else { + // We shouldn't have errors here, except for evaluate/fulfill mismatches, + // but that's not a reason for an ICE (`predicate_may_hold` is conservative + // by design). + // FIXME(-Znext-solver): This *actually* shouldn't happen then. + return None; }; - - let errors = fulfill_cx.select_where_possible(self.infcx); + let errors = ocx.select_where_possible(); if !errors.is_empty() { // This shouldn't happen, except for evaluate/fulfill mismatches, // but that's not a reason for an ICE (`predicate_may_hold` is conservative @@ -194,7 +188,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - Some((normalized_ty, fulfill_cx.pending_obligations())) + Some((normalized_ty, ocx.pending_obligations())) } /// Returns the final type we ended up with, which may be an inference diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 825276aef42..e5514fecfbe 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -45,8 +45,7 @@ use rustc_hir::Expr; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; -use rustc_infer::traits::TraitEngineExt as _; -use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause, TraitEngine}; +use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause}; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::lint::in_external_macro; use rustc_middle::traits::BuiltinImplSource; @@ -65,7 +64,6 @@ use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::TraitEngineExt as _; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, }; @@ -164,11 +162,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Filter these cases out to make sure our coercion is more accurate. match res { Ok(InferOk { value, obligations }) if self.next_trait_solver() => { - let mut fulfill_cx = >::new(self); - fulfill_cx.register_predicate_obligations(self, obligations); - let errs = fulfill_cx.select_where_possible(self); - if errs.is_empty() { - Ok(InferOk { value, obligations: fulfill_cx.pending_obligations() }) + let ocx = ObligationCtxt::new(self); + ocx.register_obligations(obligations); + if ocx.select_where_possible().is_empty() { + Ok(InferOk { value, obligations: ocx.pending_obligations() }) } else { Err(TypeError::Mismatch) } @@ -631,13 +628,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // but we need to constrain vars before processing goals mentioning // them. Some(ty::PredicateKind::AliasRelate(..)) => { - let mut fulfill_cx = >::new(self); - fulfill_cx.register_predicate_obligation(self, obligation); - let errs = fulfill_cx.select_where_possible(self); - if !errs.is_empty() { + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if !ocx.select_where_possible().is_empty() { return Err(TypeError::Mismatch); } - coercion.obligations.extend(fulfill_cx.pending_obligations()); + coercion.obligations.extend(ocx.pending_obligations()); continue; } _ => { diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index e3611ab28e5..f77a6115861 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -206,6 +206,18 @@ impl<'tcx> FulfillmentError<'tcx> { ) -> FulfillmentError<'tcx> { FulfillmentError { obligation, code, root_obligation } } + + pub fn is_true_error(&self) -> bool { + match self.code { + FulfillmentErrorCode::SelectionError(_) + | FulfillmentErrorCode::ProjectionError(_) + | FulfillmentErrorCode::SubtypeError(_, _) + | FulfillmentErrorCode::ConstEquateError(_, _) => true, + FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { + false + } + } + } } impl<'tcx> PolyTraitObligation<'tcx> { diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index b2400cec42f..3dc55509dad 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,8 +1,9 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, ObligationCtxt, SelectionContext, TraitEngineExt as _}; +use crate::traits::{self, ObligationCtxt, SelectionContext}; + use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt as _}; +use rustc_infer::traits::Obligation; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; @@ -93,9 +94,9 @@ impl<'tcx> InferCtxt<'tcx> { ty::TraitRef::new(self.tcx, trait_def_id, [ty]), )) { Ok(Some(selection)) => { - let mut fulfill_cx = >::new(self); - fulfill_cx.register_predicate_obligations(self, selection.nested_obligations()); - Some(fulfill_cx.select_all_or_error(self)) + let ocx = ObligationCtxt::new(self); + ocx.register_obligations(selection.nested_obligations()); + Some(ocx.select_all_or_error()) } Ok(None) | Err(_) => None, } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 7c6dd4d3feb..97de25295b8 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -14,7 +14,6 @@ use rustc_ast_ir::visit::VisitorResult; use rustc_infer::infer::resolve::EagerResolver; use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; -use rustc_infer::traits::{TraitEngine, TraitEngineExt}; use rustc_macros::extension; use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::traits::query::NoSolution; @@ -26,9 +25,9 @@ use rustc_middle::ty::TypeFoldable; use rustc_span::{Span, DUMMY_SP}; use crate::solve::eval_ctxt::canonical; -use crate::solve::FulfillmentCtxt; use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource}; use crate::solve::{GenerateProofTree, InferCtxtEvalExt}; +use crate::traits::ObligationCtxt; pub struct InspectConfig { pub max_depth: usize, @@ -74,14 +73,13 @@ impl<'tcx> NormalizesToTermHack<'tcx> { .eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term) .map_err(|_| NoSolution) .and_then(|InferOk { value: (), obligations }| { - let mut fulfill_cx = FulfillmentCtxt::new(infcx); - fulfill_cx.register_predicate_obligations(infcx, obligations); - if fulfill_cx.select_where_possible(infcx).is_empty() { - if fulfill_cx.pending_obligations().is_empty() { - Ok(Certainty::Yes) - } else { - Ok(Certainty::AMBIGUOUS) - } + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligations(obligations); + let errors = ocx.select_all_or_error(); + if errors.is_empty() { + Ok(Certainty::Yes) + } else if errors.iter().all(|e| !e.is_true_error()) { + Ok(Certainty::AMBIGUOUS) } else { Err(NoSolution) } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 86e7c42376a..59725ce9de0 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,12 +6,9 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::InferOk; -use crate::regions::InferCtxtRegionExt; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; -use crate::solve::{deeply_normalize_for_diagnostics, inspect, FulfillmentCtxt}; -use crate::traits::engine::TraitEngineExt as _; +use crate::solve::{deeply_normalize_for_diagnostics, inspect}; use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::structural_normalize::StructurallyNormalizeExt; use crate::traits::NormalizeExt; use crate::traits::SkipLeakCheck; use crate::traits::{ @@ -22,7 +19,7 @@ use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::{util, FulfillmentErrorCode}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; @@ -35,6 +32,7 @@ use std::fmt::Debug; use std::ops::ControlFlow; use super::error_reporting::suggest_new_overflow_limit; +use super::ObligationCtxt; /// Whether we do the orphan check relative to this crate or to some remote crate. #[derive(Copy, Clone, Debug)] @@ -361,23 +359,27 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( let infcx = selcx.infcx; if infcx.next_trait_solver() { - let mut fulfill_cx = FulfillmentCtxt::new(infcx); - fulfill_cx.register_predicate_obligations(infcx, obligations.iter().cloned()); - + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligations(obligations.iter().cloned()); + let errors_and_ambiguities = ocx.select_all_or_error(); // We only care about the obligations that are *definitely* true errors. // Ambiguities do not prove the disjointness of two impls. - let errors = fulfill_cx.select_where_possible(infcx); + let (errors, ambiguities): (Vec<_>, Vec<_>) = + errors_and_ambiguities.into_iter().partition(|error| error.is_true_error()); + if errors.is_empty() { - let overflow_errors = fulfill_cx.collect_remaining_errors(infcx); - let overflowing_predicates = overflow_errors - .into_iter() - .filter(|e| match e.code { - FulfillmentErrorCode::Ambiguity { overflow: Some(true) } => true, - _ => false, - }) - .map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate)) - .collect(); - IntersectionHasImpossibleObligations::No { overflowing_predicates } + IntersectionHasImpossibleObligations::No { + overflowing_predicates: ambiguities + .into_iter() + .filter(|error| { + matches!( + error.code, + FulfillmentErrorCode::Ambiguity { overflow: Some(true) } + ) + }) + .map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate)) + .collect(), + } } else { IntersectionHasImpossibleObligations::Yes } @@ -589,13 +591,14 @@ fn try_prove_negated_where_clause<'tcx>( // Without this, we over-eagerly register coherence ambiguity candidates when // impl candidates do exist. let ref infcx = root_infcx.fork_with_intercrate(false); - let mut fulfill_cx = FulfillmentCtxt::new(infcx); - - fulfill_cx.register_predicate_obligation( - infcx, - Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, negative_predicate), - ); - if !fulfill_cx.select_all_or_error(infcx).is_empty() { + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligation(Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + param_env, + negative_predicate, + )); + if !ocx.select_all_or_error().is_empty() { return false; } @@ -603,8 +606,7 @@ fn try_prove_negated_where_clause<'tcx>( // if that wasn't implemented just for LocalDefId, and we'd need to do // the normalization ourselves since this is totally fallible... let outlives_env = OutlivesEnvironment::new(param_env); - - let errors = infcx.resolve_regions(&outlives_env); + let errors = ocx.resolve_regions(&outlives_env); if !errors.is_empty() { return false; } @@ -1129,22 +1131,17 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { result: Ok(_), } = cand.kind() { - let lazily_normalize_ty = |ty: Ty<'tcx>| { - let mut fulfill_cx = >::new(infcx); + let lazily_normalize_ty = |mut ty: Ty<'tcx>| { if matches!(ty.kind(), ty::Alias(..)) { - // FIXME(-Znext-solver=coherence): we currently don't - // normalize opaque types here, resulting in diverging behavior - // for TAITs. - match infcx - .at(&ObligationCause::dummy(), param_env) - .structurally_normalize(ty, &mut *fulfill_cx) - { - Ok(ty) => Ok(ty), - Err(_errs) => Err(()), + let ocx = ObligationCtxt::new(infcx); + ty = ocx + .structurally_normalize(&ObligationCause::dummy(), param_env, ty) + .map_err(|_| ())?; + if !ocx.select_where_possible().is_empty() { + return Err(()); } - } else { - Ok(ty) } + Ok(ty) }; infcx.probe(|_| { diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 432d51ff8f9..9eaaef1eb22 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -7,6 +7,7 @@ use crate::regions::InferCtxtRegionExt; use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::NormalizeExt; +use crate::traits::StructurallyNormalizeExt; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -15,6 +16,7 @@ use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::RegionResolutionError; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_infer::traits::{ FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, @@ -117,6 +119,17 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut()) } + pub fn structurally_normalize( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: Ty<'tcx>, + ) -> Result, Vec>> { + self.infcx + .at(cause, param_env) + .structurally_normalize(value, &mut **self.engine.borrow_mut()) + } + pub fn eq>( &self, cause: &ObligationCause<'tcx>, @@ -182,6 +195,11 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { self.engine.borrow_mut().select_all_or_error(self.infcx) } + #[must_use] + pub fn pending_obligations(&self) -> Vec> { + self.engine.borrow().pending_obligations() + } + /// Resolves regions and reports errors. /// /// Takes ownership of the context as doing trait solving afterwards @@ -199,6 +217,18 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { } } + /// Resolves regions and reports errors. + /// + /// Takes ownership of the context as doing trait solving afterwards + /// will result in region constraints getting ignored. + #[must_use] + pub fn resolve_regions( + self, + outlives_env: &OutlivesEnvironment<'tcx>, + ) -> Vec> { + self.infcx.resolve_regions(outlives_env) + } + pub fn assumed_wf_types_and_report_errors( &self, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 5f2a8944135..87d240cf8ac 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,9 +1,10 @@ -use rustc_infer::traits::{TraitEngine, TraitEngineExt}; use rustc_macros::extension; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; -use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; +use crate::traits::{ + EvaluationResult, ObligationCtxt, OverflowError, PredicateObligation, SelectionContext, +}; #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { @@ -67,21 +68,22 @@ impl<'tcx> InferCtxt<'tcx> { if self.next_trait_solver() { self.probe(|snapshot| { - let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self); - fulfill_cx.register_predicate_obligation(self, obligation.clone()); - // True errors - // FIXME(-Znext-solver): Overflows are reported as ambig here, is that OK? - if !fulfill_cx.select_where_possible(self).is_empty() { - Ok(EvaluationResult::EvaluatedToErr) - } else if !fulfill_cx.select_all_or_error(self).is_empty() { - Ok(EvaluationResult::EvaluatedToAmbig) - } else if self.opaque_types_added_in_snapshot(snapshot) { - Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes) - } else if self.region_constraints_added_in_snapshot(snapshot) { - Ok(EvaluationResult::EvaluatedToOkModuloRegions) - } else { - Ok(EvaluationResult::EvaluatedToOk) + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation.clone()); + let mut result = EvaluationResult::EvaluatedToOk; + for error in ocx.select_all_or_error() { + if error.is_true_error() { + return Ok(EvaluationResult::EvaluatedToErr); + } else { + result = result.max(EvaluationResult::EvaluatedToAmbig); + } } + if self.opaque_types_added_in_snapshot(snapshot) { + result = result.max(EvaluationResult::EvaluatedToOkModuloOpaqueTypes); + } else if self.region_constraints_added_in_snapshot(snapshot) { + result = result.max(EvaluationResult::EvaluatedToOkModuloRegions); + } + Ok(result) }) } else { assert!(!self.intercrate); diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 51f38964415..b96b1b67a74 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -4,14 +4,13 @@ // general routines. use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::{FulfillmentErrorCode, TraitEngineExt as _}; +use rustc_infer::traits::FulfillmentErrorCode; use rustc_middle::bug; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::{ - ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt, - Unimplemented, + ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, Unimplemented, }; use tracing::debug; @@ -51,15 +50,15 @@ pub fn codegen_select_candidate<'tcx>( // Currently, we use a fulfillment context to completely resolve // all nested obligations. This is because they can inform the // inference of the impl's type parameters. - let mut fulfill_cx = >::new(&infcx); - let impl_source = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); + let ocx = ObligationCtxt::new(&infcx); + let impl_source = selection.map(|obligation| { + ocx.register_obligation(obligation); }); // In principle, we only need to do this so long as `impl_source` // contains unbound type parameters. It could be a slight // optimization to stop iterating early. - let errors = fulfill_cx.select_all_or_error(&infcx); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { // `rustc_monomorphize::collector` assumes there are no type errors. // Cycle errors are the only post-monomorphization errors possible; emit them now so