Skip to content

Commit

Permalink
Auto merge of rust-lang#123669 - oli-obk:eager_opaque_checks4, r=<try>
Browse files Browse the repository at this point in the history
Avoid a scrape_region_constraints and instead register the region constraints directly

Should fix the perf regression from rust-lang#122077 (comment)
  • Loading branch information
bors committed Apr 9, 2024
2 parents 86b603c + 28df1d3 commit de35723
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 145 deletions.
223 changes: 139 additions & 84 deletions compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,64 +248,71 @@ 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
// variable...

let constraint_category = cause.to_constraint_category();

for (index, original_value) in original_values.var_values.iter().enumerate() {
for (index, &original_value) in original_values.var_values.iter().enumerate() {
// ...with the value `v_r` of that variable from the query.
let result_value = query_response.instantiate_projected(self.tcx, &result_args, |v| {
v.var_values[BoundVar::new(index)]
});
match (original_value.unpack(), result_value.unpack()) {
(GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
if re1.is_erased() && re2.is_erased() =>
{
// No action needed.
}

(GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
// To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
if v_o != v_r {
output_query_region_constraints
.outlives
.push((ty::OutlivesPredicate(v_o.into(), v_r), constraint_category));
output_query_region_constraints
.outlives
.push((ty::OutlivesPredicate(v_r.into(), v_o), constraint_category));
}
}

(GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
obligations.extend(
self.at(&cause, param_env)
.eq(DefineOpaqueTypes::Yes, v1, v2)?
.into_obligations(),
);
}

(GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
obligations.extend(
self.at(&cause, param_env)
.eq(DefineOpaqueTypes::Yes, v1, v2)?
.into_obligations(),
);
}

_ => {
bug!("kind mismatch, cannot unify {:?} and {:?}", original_value, result_value);
}
}
self.equate_generic_arg(
original_value,
result_value,
output_query_region_constraints,
constraint_category,
&mut obligations,
cause,
param_env,
)?;
}

// ...also include the other query region constraints from the query.
Expand Down Expand Up @@ -335,6 +342,57 @@ impl<'tcx> InferCtxt<'tcx> {
Ok(InferOk { value: user_result, obligations })
}

fn equate_generic_arg(
&self,
original_value: GenericArg<'tcx>,
result_value: GenericArg<'tcx>,
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
constraint_category: ConstraintCategory<'tcx>,
obligations: &mut Vec<Obligation<'tcx, ty::Predicate<'tcx>>>,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<(), ty::error::TypeError<'tcx>> {
Ok(match (original_value.unpack(), result_value.unpack()) {
(GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
if re1.is_erased() && re2.is_erased() =>
{
// No action needed.
}

(GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
// To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
if v_o != v_r {
output_query_region_constraints
.outlives
.push((ty::OutlivesPredicate(v_o.into(), v_r), constraint_category));
output_query_region_constraints
.outlives
.push((ty::OutlivesPredicate(v_r.into(), v_o), constraint_category));
}
}

(GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
obligations.extend(
self.at(&cause, param_env)
.eq(DefineOpaqueTypes::Yes, v1, v2)?
.into_obligations(),
);
}

(GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
obligations.extend(
self.at(&cause, param_env)
.eq(DefineOpaqueTypes::Yes, v1, v2)?
.into_obligations(),
);
}

_ => {
bug!("kind mismatch, cannot unify {:?} and {:?}", original_value, result_value);
}
})
}

/// Given the original values and the (canonicalized) result from
/// computing a query, returns an instantiation that can be applied
/// to the query result to convert the result back into the
Expand All @@ -360,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);

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,
);
}

value.obligations.extend(
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 @@ -390,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 @@ -470,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 @@ -495,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
Loading

0 comments on commit de35723

Please sign in to comment.