Skip to content

correctly handle normalization in implied bounds query #109482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 18 additions & 86 deletions compiler/rustc_borrowck/src/type_check/free_region_relations.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty::{self, RegionVid, Ty};
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use std::rc::Rc;
use type_op::TypeOpOutput;
@@ -35,16 +34,9 @@ pub(crate) struct UniversalRegionRelations<'tcx> {
inverse_outlives: TransitiveRelation<RegionVid>,
}

/// As part of computing the free region relations, we also have to
/// normalize the input-output types, which we then need later. So we
/// return those. This vector consists of first the input types and
/// then the output type as the last element.
type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>;

pub(crate) struct CreateResult<'tcx> {
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>,
pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>,
}

pub(crate) fn create<'tcx>(
@@ -222,68 +214,27 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
.chain(Some(self.universal_regions.unnormalized_output_ty));

// For each of the input/output types:
// - Normalize the type. This will create some region
// constraints, which we buffer up because we are
// not ready to process them yet.
// - Then compute the implied bounds. This will adjust
// - Compute the implied bounds. This will adjust
// the `region_bound_pairs` and so forth.
// - After this is done, we'll process the constraints, once
// the `relations` is built.
let mut normalized_inputs_and_output =
Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
let mut constraints = vec![];
for ty in unnormalized_input_output_tys {
debug!("build: input_or_output={:?}", ty);
// We add implied bounds from both the unnormalized and normalized ty.
// See issue #87748
let constraints_unnorm = self.add_implied_bounds(ty);
if let Some(c) = constraints_unnorm {
constraints.push(c)
}
let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self
.param_env
.and(type_op::normalize::Normalize::new(ty))
.fully_perform(self.infcx)
.unwrap_or_else(|_| {
let guar = self
.infcx
.tcx
.sess
.delay_span_bug(span, &format!("failed to normalize {:?}", ty));
TypeOpOutput {
output: self.infcx.tcx.ty_error(guar),
constraints: None,
error_info: None,
}
});
if let Some(c) = constraints_normalize {
constraints.push(c)
}

// Note: we need this in examples like
// ```
// trait Foo {
// type Bar;
// fn foo(&self) -> &Self::Bar;
// }
// impl Foo for () {
// type Bar = ();
// fn foo(&self) ->&() {}
// }
// ```
// Both &Self::Bar and &() are WF
if ty != norm_ty {
let constraints_norm = self.add_implied_bounds(norm_ty);
if let Some(c) = constraints_norm {
constraints.push(c)
}
}

normalized_inputs_and_output.push(norm_ty);
}

for c in constraints {
self.push_region_constraints(c, span);
let constraint_sets: Vec<_> =
unnormalized_input_output_tys.flat_map(|ty| self.add_implied_bounds(ty)).collect();

// Subtle: We can convert constraints only after the relations are built.
for data in &constraint_sets {
constraint_conversion::ConstraintConversion::new(
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
Locations::All(span),
span,
ConstraintCategory::Internal,
&mut self.constraints,
)
.convert_all(data);
}

CreateResult {
@@ -293,28 +244,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
inverse_outlives: self.inverse_outlives.freeze(),
}),
region_bound_pairs: self.region_bound_pairs,
normalized_inputs_and_output,
}
}

#[instrument(skip(self, data), level = "debug")]
fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) {
debug!("constraints generated: {:#?}", data);

constraint_conversion::ConstraintConversion::new(
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
Locations::All(span),
span,
ConstraintCategory::Internal,
&mut self.constraints,
)
.convert_all(data);
}

/// Update the type of a single local, which should represent
/// either the return type of the MIR or one of its arguments. At
/// the same time, compute and add any implied bounds that come
25 changes: 11 additions & 14 deletions compiler/rustc_borrowck/src/type_check/input_output.rs
Original file line number Diff line number Diff line change
@@ -13,8 +13,6 @@ use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;

use crate::universal_regions::UniversalRegions;

use super::{Locations, TypeChecker};

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -60,21 +58,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
}

#[instrument(skip(self, body, universal_regions), level = "debug")]
pub(super) fn equate_inputs_and_outputs(
&mut self,
body: &Body<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
normalized_inputs_and_output: &[Ty<'tcx>],
) {
let (&normalized_output_ty, normalized_input_tys) =
normalized_inputs_and_output.split_last().unwrap();
#[instrument(skip(self, body), level = "debug")]
pub(super) fn equate_inputs_and_outputs(&mut self, body: &Body<'tcx>) {
let universal_regions = self.borrowck_context.universal_regions;

debug!(?normalized_output_ty);
debug!(?normalized_input_tys);
debug!(?universal_regions.unnormalized_input_tys, ?body.local_decls);

// Equate expected input tys with those in the MIR.
for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
for (argument_index, &unnormalized_input_ty) in
universal_regions.unnormalized_input_tys.iter().enumerate()
{
if argument_index + 1 >= body.local_decls.len() {
self.tcx()
.sess
@@ -88,6 +81,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let mir_input_ty = body.local_decls[local].ty;

let mir_input_span = body.local_decls[local].source_info.span;
let normalized_input_ty =
self.normalize(unnormalized_input_ty, Locations::All(mir_input_span));
self.equate_normalized_input_or_output(
normalized_input_ty,
mir_input_ty,
@@ -125,6 +120,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// Return types are a bit more complex. They may contain opaque `impl Trait` types.
let mir_output_ty = body.local_decls[RETURN_PLACE].ty;
let output_span = body.local_decls[RETURN_PLACE].source_info.span;
let normalized_output_ty =
self.normalize(universal_regions.unnormalized_output_ty, Locations::All(output_span));
if let Err(terr) = self.eq_types(
normalized_output_ty,
mir_output_ty,
23 changes: 9 additions & 14 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
@@ -147,19 +147,14 @@ pub(crate) fn type_check<'mir, 'tcx>(
universe_causes: FxIndexMap::default(),
};

let CreateResult {
universal_region_relations,
region_bound_pairs,
normalized_inputs_and_output,
} = free_region_relations::create(
infcx,
param_env,
implicit_region_bound,
universal_regions,
&mut constraints,
);

debug!(?normalized_inputs_and_output);
let CreateResult { universal_region_relations, region_bound_pairs } =
free_region_relations::create(
infcx,
param_env,
implicit_region_bound,
universal_regions,
&mut constraints,
);

for u in ty::UniverseIndex::ROOT..=infcx.universe() {
constraints.universe_causes.insert(u, UniverseInfo::other());
@@ -194,7 +189,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
checker.typeck_mir(body);
}

checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output);
checker.equate_inputs_and_outputs(&body);
checker.check_signature_annotation(&body);

liveness::generate(
4 changes: 1 addition & 3 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
@@ -259,10 +259,8 @@ fn compare_method_predicate_entailment<'tcx>(
// we have to do this before normalization, since the normalized ty may
// not contain the input parameters. See issue #87748.
wf_tys.extend(trait_sig.inputs_and_output.iter());

let trait_sig = ocx.normalize(&norm_cause, param_env, trait_sig);
// We also have to add the normalized trait signature
// as we don't normalize during implied bounds computation.
wf_tys.extend(trait_sig.inputs_and_output.iter());
let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));

debug!("compare_impl_method: trait_fty={:?}", trait_fty);
18 changes: 6 additions & 12 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
@@ -144,19 +144,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn compute_well_formed_goal(
&mut self,
goal: Goal<'tcx, ty::GenericArg<'tcx>>,
Goal { predicate, param_env }: Goal<'tcx, ty::GenericArg<'tcx>>,
) -> QueryResult<'tcx> {
match crate::traits::wf::unnormalized_obligations(
self.infcx,
goal.param_env,
goal.predicate,
) {
Some(obligations) => {
self.add_goals(obligations.into_iter().map(|o| o.into()));
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
}
debug_assert_eq!(predicate, self.infcx.resolve_vars_if_possible(predicate));
let obligations =
crate::traits::wf::unnormalized_obligations(self.tcx(), param_env, predicate);
self.add_goals(obligations.into_iter().map(|o| o.into()));
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}

#[instrument(level = "debug", skip(self), ret)]
17 changes: 8 additions & 9 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
@@ -77,21 +77,20 @@ pub fn obligations<'tcx>(

/// Compute the predicates that are required for a type to be well-formed.
///
/// This is only intended to be used in the new solver, since it does not
/// take into account recursion depth or proper error-reporting spans.
/// This is only intended to be used in implied bounds computation and in
/// the new solver, since it does not take into account recursion depth or
/// proper error-reporting spans.
pub fn unnormalized_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
) -> Vec<traits::PredicateObligation<'tcx>> {
if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
return Some(vec![]);
return vec![];
}

debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));

let mut wf = WfPredicates {
tcx: infcx.tcx,
tcx,
param_env,
body_id: CRATE_DEF_ID,
span: DUMMY_SP,
@@ -100,7 +99,7 @@ pub fn unnormalized_obligations<'tcx>(
item: None,
};
wf.compute(arg);
Some(wf.out)
wf.out
}

/// Returns the obligations that make this trait reference
Loading