From 28df1d393e904c001442027b9d15ab7b210ca494 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Apr 2024 09:17:20 +0000 Subject: [PATCH] Avoid a `scrape_region_constraints` and instead register the region constraints directly --- .../src/infer/canonical/query_response.rs | 122 +++++++++++------- .../src/traits/query/type_op/mod.rs | 103 ++++++--------- .../ui/inference/issue-80409.no-compat.stderr | 2 +- 3 files changed, 122 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 851a8aa9fa568..c83c579f32095 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -248,13 +248,50 @@ impl<'tcx> InferCtxt<'tcx> { where R: Debug + TypeFoldable>, { - let InferOk { value: result_args, mut obligations } = self - .query_response_instantiation_guess( - cause, - param_env, - original_values, - query_response, - )?; + let result_args = + self.query_response_instantiation_guess(cause, original_values, query_response); + + let mut obligations = vec![]; + + // Carry all newly resolved opaque types to the caller's scope + for &(opaque_type_key, hidden_ty) in &query_response.value.opaque_types { + let opaque_type_key = instantiate_value(self.tcx, &result_args, opaque_type_key); + let hidden_ty = instantiate_value(self.tcx, &result_args, hidden_ty); + debug!(?opaque_type_key, ?hidden_ty, "constrain opaque type"); + // We can't use equate here, because the hidden type may have been an inference + // variable that got constrained to the opaque type itself. In that case we want to ensure + // any lifetime differences get recorded in `ouput_query_region_constraints` instead of + // being registered in the `InferCtxt`. + match hidden_ty.kind() { + ty::Alias(ty::Opaque, alias_ty) + if alias_ty.def_id == opaque_type_key.def_id.into() => + { + assert_eq!(alias_ty.args.len(), opaque_type_key.args.len()); + for (key_arg, hidden_arg) in + opaque_type_key.args.iter().zip(alias_ty.args.iter()) + { + self.equate_generic_arg( + key_arg, + hidden_arg, + output_query_region_constraints, + ConstraintCategory::OpaqueType, + &mut obligations, + cause, + param_env, + )?; + } + } + _ => { + self.insert_hidden_type( + opaque_type_key, + cause, + param_env, + hidden_ty, + &mut obligations, + )?; + } + } + } // Compute `QueryOutlivesConstraint` values that unify each of // the original values `v_o` that was canonicalized into a @@ -381,25 +418,47 @@ impl<'tcx> InferCtxt<'tcx> { original_values, query_response, ); - let mut value = self.query_response_instantiation_guess( - cause, - param_env, - original_values, - query_response, - )?; + let result_args = + self.query_response_instantiation_guess(cause, original_values, query_response); - value.obligations.extend( + let mut obligations = vec![]; + + // Carry all newly resolved opaque types to the caller's scope + for &(opaque_type_key, hidden_ty) in &query_response.value.opaque_types { + let opaque_type_key = instantiate_value(self.tcx, &result_args, opaque_type_key); + let hidden_ty = instantiate_value(self.tcx, &result_args, hidden_ty); + debug!(?opaque_type_key, ?hidden_ty, "constrain opaque type"); + // We use equate here instead of, for example, just registering the + // opaque type's hidden value directly, because the hidden type may have been an inference + // variable that got constrained to the opaque type itself. In that case we want to equate + // the generic args of the opaque with the generic params of its hidden type version. + obligations.extend( + self.at(cause, param_env) + .eq( + DefineOpaqueTypes::Yes, + Ty::new_opaque( + self.tcx, + opaque_type_key.def_id.to_def_id(), + opaque_type_key.args, + ), + hidden_ty, + )? + .obligations, + ); + } + + obligations.extend( self.unify_query_response_instantiation_guess( cause, param_env, original_values, - &value.value, + &result_args, query_response, )? .into_obligations(), ); - Ok(value) + Ok(InferOk { value: result_args, obligations }) } /// Given the original values and the (canonicalized) result from @@ -411,14 +470,13 @@ impl<'tcx> InferCtxt<'tcx> { /// will instantiate fresh inference variables for each canonical /// variable instead. Therefore, the result of this method must be /// properly unified - #[instrument(level = "debug", skip(self, param_env))] + #[instrument(level = "debug", skip(self))] fn query_response_instantiation_guess( &self, cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, original_values: &OriginalQueryValues<'tcx>, query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, - ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> + ) -> CanonicalVarValues<'tcx> where R: Debug + TypeFoldable>, { @@ -491,7 +549,7 @@ impl<'tcx> InferCtxt<'tcx> { // Create result arguments: if we found a value for a // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. - let result_args = CanonicalVarValues { + CanonicalVarValues { var_values: self.tcx.mk_args_from_iter( query_response.variables.iter().enumerate().map(|(index, info)| { if info.universe() != ty::UniverseIndex::ROOT { @@ -516,31 +574,7 @@ impl<'tcx> InferCtxt<'tcx> { } }), ), - }; - - let mut obligations = vec![]; - - // Carry all newly resolved opaque types to the caller's scope - for &(a, b) in &query_response.value.opaque_types { - let a = instantiate_value(self.tcx, &result_args, a); - let b = instantiate_value(self.tcx, &result_args, b); - debug!(?a, ?b, "constrain opaque type"); - // We use equate here instead of, for example, just registering the - // opaque type's hidden value directly, because the hidden type may have been an inference - // variable that got constrained to the opaque type itself. In that case we want to equate - // the generic args of the opaque with the generic params of its hidden type version. - obligations.extend( - self.at(cause, param_env) - .eq( - DefineOpaqueTypes::Yes, - Ty::new_opaque(self.tcx, a.def_id.to_def_id(), a.args), - b, - )? - .obligations, - ); } - - Ok(InferOk { value: result_args, obligations }) } /// Given a "guess" at the values for the canonical variables in diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index ae4cdb9258e29..faf218131b8b1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -159,70 +159,53 @@ where .0); } - let mut error_info = None; let mut region_constraints = QueryRegionConstraints::default(); - - // HACK(type_alias_impl_trait): When moving an opaque type to hidden type mapping from the query to the current inferctxt, - // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a - // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually - // collecting region constraints via `region_constraints`. - let (mut output, _) = scrape_region_constraints( - infcx, - |_ocx| { - let (output, ei, mut obligations, _) = - Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; - error_info = ei; - - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - while !obligations.is_empty() { - trace!("{:#?}", obligations); - let mut progress = false; - for obligation in std::mem::take(&mut obligations) { - let obligation = infcx.resolve_vars_if_possible(obligation); - match ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - &mut region_constraints, - span, - ) { - Ok(((), _, new, certainty)) => { - obligations.extend(new); - progress = true; - if let Certainty::Ambiguous = certainty { - obligations.push(obligation); - } - } - Err(_) => obligations.push(obligation), + let (output, error_info, mut obligations, _) = + Q::fully_perform_into(self, infcx, &mut region_constraints, span).map_err(|_| { + infcx.dcx().span_delayed_bug(span, format!("error performing {self:?}")) + })?; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + while !obligations.is_empty() { + trace!("{:#?}", obligations); + let mut progress = false; + for obligation in std::mem::take(&mut obligations) { + let obligation = infcx.resolve_vars_if_possible(obligation); + match ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + &mut region_constraints, + span, + ) { + Ok(((), _, new, certainty)) => { + obligations.extend(new); + progress = true; + if let Certainty::Ambiguous = certainty { + obligations.push(obligation); } } - if !progress { - infcx.dcx().span_bug( - span, - format!("ambiguity processing {obligations:?} from {self:?}"), - ); - } + Err(_) => obligations.push(obligation), } - Ok(output) - }, - "fully_perform", - span, - )?; - output.error_info = error_info; - if let Some(constraints) = output.constraints { - region_constraints - .member_constraints - .extend(constraints.member_constraints.iter().cloned()); - region_constraints.outlives.extend(constraints.outlives.iter().cloned()); + } + if !progress { + infcx + .dcx() + .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}")); + } } - output.constraints = if region_constraints.is_empty() { - None - } else { - Some(infcx.tcx.arena.alloc(region_constraints)) - }; - Ok(output) + + Ok(TypeOpOutput { + output, + constraints: if region_constraints.is_empty() { + None + } else { + Some(infcx.tcx.arena.alloc(region_constraints)) + }, + error_info, + }) } } diff --git a/tests/ui/inference/issue-80409.no-compat.stderr b/tests/ui/inference/issue-80409.no-compat.stderr index c772225be7549..523ca229b06f4 100644 --- a/tests/ui/inference/issue-80409.no-compat.stderr +++ b/tests/ui/inference/issue-80409.no-compat.stderr @@ -1,4 +1,4 @@ -error: internal compiler error: error performing operation: fully_perform +error: internal compiler error: error performing ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: ImpliedOutlivesBounds { ty: &'?2 mut StateContext<'?3, usize> } } --> $DIR/issue-80409.rs:49:30 | LL | builder.state().on_entry(|_| {});