Skip to content

Commit

Permalink
Avoid a scrape_region_constraints and instead register the region c…
Browse files Browse the repository at this point in the history
…onstraints directly
  • Loading branch information
oli-obk committed Apr 9, 2024
1 parent 592f251 commit 28df1d3
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 105 deletions.
122 changes: 78 additions & 44 deletions compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,50 @@ impl<'tcx> InferCtxt<'tcx> {
where
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
{
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
Expand Down Expand Up @@ -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
Expand All @@ -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<R>(
&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<TyCtxt<'tcx>>,
{
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down
103 changes: 43 additions & 60 deletions compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
}
}
2 changes: 1 addition & 1 deletion tests/ui/inference/issue-80409.no-compat.stderr
Original file line number Diff line number Diff line change
@@ -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(|_| {});
Expand Down

0 comments on commit 28df1d3

Please sign in to comment.