Skip to content
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

stop using FnCtxt outside of hir typeck #99015

Merged
merged 7 commits into from
Jul 15, 2022
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
let origin = RelateParamBound(type_test_span, generic_ty, None);
self.buffer_error(self.infcx.construct_generic_bound_failure(
self.body.source.def_id().expect_local(),
type_test_span,
Some(origin),
type_test.generic_kind,
lower_bound_region,
self.body.source.def_id().as_local(),
));
} else {
// FIXME. We should handle this case better. It
Expand Down
84 changes: 28 additions & 56 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, Mul
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{Item, ItemKind, Node};
use rustc_hir::Node;
use rustc_middle::dep_graph::DepContext;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
Expand Down Expand Up @@ -348,7 +348,11 @@ pub fn same_type_modulo_infer<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
}

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn report_region_errors(&self, errors: &[RegionResolutionError<'tcx>]) {
pub fn report_region_errors(
&self,
generic_param_scope: LocalDefId,
errors: &[RegionResolutionError<'tcx>],
) {
debug!("report_region_errors(): {} errors to start", errors.len());

// try to pre-process the errors, which will group some of them
Expand Down Expand Up @@ -379,6 +383,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => {
self.report_generic_bound_failure(
generic_param_scope,
origin.span(),
Some(origin),
param_ty,
Expand Down Expand Up @@ -2269,56 +2274,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

pub fn report_generic_bound_failure(
&self,
generic_param_scope: LocalDefId,
span: Span,
origin: Option<SubregionOrigin<'tcx>>,
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) {
let owner =
self.in_progress_typeck_results.map(|typeck_results| typeck_results.borrow().hir_owner);
self.construct_generic_bound_failure(span, origin, bound_kind, sub, owner).emit();
self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
.emit();
}

pub fn construct_generic_bound_failure(
&self,
generic_param_scope: LocalDefId,
span: Span,
origin: Option<SubregionOrigin<'tcx>>,
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
owner: Option<LocalDefId>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let hir = self.tcx.hir();
// Attempt to obtain the span of the parameter so we can
// suggest adding an explicit lifetime bound to it.
let generics = owner.map(|owner| {
let hir_id = hir.local_def_id_to_hir_id(owner);
let parent_id = hir.get_parent_item(hir_id);
(
// Parent item could be a `mod`, so we check the HIR before calling:
if let Some(Node::Item(Item {
kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
..
})) = hir.find_by_def_id(parent_id)
{
Some(self.tcx.generics_of(parent_id))
} else {
None
},
self.tcx.generics_of(owner.to_def_id()),
hir.span(hir_id),
)
});

let span = match generics {
// This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal
// for other diagnostics, so we need to recover it here.
Some((_, _, node)) if span.is_dummy() => node,
_ => span,
};

let generics = self.tcx.generics_of(generic_param_scope);
// type_param_span is (span, has_bounds)
let type_param_span = match (generics, bound_kind) {
(Some((_, ref generics, _)), GenericKind::Param(ref param)) => {
let type_param_span = match bound_kind {
GenericKind::Param(ref param) => {
// Account for the case where `param` corresponds to `Self`,
// which doesn't have the expected type argument.
if !(generics.has_self && param.index == 0) {
Expand Down Expand Up @@ -2346,30 +2325,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
_ => None,
};
let new_lt = generics
.as_ref()
.and_then(|(parent_g, g, _)| {
let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
let mut lts_names = g
.params
.iter()

let new_lt = {
let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
let lts_names =
iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
.flat_map(|g| &g.params)
.filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
.map(|p| p.name.as_str())
.collect::<Vec<_>>();
if let Some(g) = parent_g {
lts_names.extend(
g.params
.iter()
.filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
.map(|p| p.name.as_str()),
);
}
possible.find(|candidate| !lts_names.contains(&&candidate[..]))
})
.unwrap_or("'lt".to_string());
possible
.find(|candidate| !lts_names.contains(&&candidate[..]))
.unwrap_or("'lt".to_string())
};

let add_lt_sugg = generics
.as_ref()
.and_then(|(_, g, _)| g.params.first())
.params
.first()
.and_then(|param| param.def_id.as_local())
.map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt)));

Expand Down Expand Up @@ -2571,7 +2543,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
);
if let Some(infer::RelateParamBound(_, t, _)) = origin {
let return_impl_trait =
owner.and_then(|owner| self.tcx.return_type_impl_trait(owner)).is_some();
self.tcx.return_type_impl_trait(generic_param_scope).is_some();
let t = self.resolve_vars_if_possible(t);
match t.kind() {
// We've got:
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1301,7 +1301,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// result. After this, no more unification operations should be
/// done -- or the compiler will panic -- but it is legal to use
/// `resolve_vars_if_possible` as well as `fully_resolve`.
pub fn resolve_regions_and_report_errors(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
pub fn resolve_regions_and_report_errors(
&self,
generic_param_scope: LocalDefId,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
let errors = self.resolve_regions(outlives_env);

if !self.is_tainted_by_errors() {
Expand All @@ -1310,7 +1314,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// this infcx was in use. This is totally hokey but
// otherwise we have a hard time separating legit region
// errors from silly ones.
self.report_region_errors(&errors);
self.report_region_errors(generic_param_scope, &errors);
}
}

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ use crate::infer::{
};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable};
use smallvec::smallvec;
Expand Down Expand Up @@ -163,14 +164,15 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {

pub fn check_region_obligations_and_report_errors(
&self,
generic_param_scope: LocalDefId,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
self.process_registered_region_obligations(
outlives_env.region_bound_pairs(),
outlives_env.param_env,
);

self.resolve_regions_and_report_errors(outlives_env)
self.resolve_regions_and_report_errors(generic_param_scope, outlives_env)
}
}

Expand Down
106 changes: 105 additions & 1 deletion compiler/rustc_trait_selection/src/traits/engine.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
use rustc_middle::ty::TyCtxt;
use std::cell::RefCell;

use super::TraitEngine;
use super::{ChalkFulfillmentContext, FulfillmentContext};
use crate::infer::InferCtxtExt;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{InferCtxt, InferOk};
use rustc_infer::traits::{
FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _,
};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};

pub trait TraitEngineExt<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;

fn new_ignoring_regions(tcx: TyCtxt<'tcx>) -> Box<Self>;
}

impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
Expand All @@ -15,4 +27,96 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
Box::new(FulfillmentContext::new())
}
}

fn new_ignoring_regions(tcx: TyCtxt<'tcx>) -> Box<Self> {
if tcx.sess.opts.unstable_opts.chalk {
Box::new(ChalkFulfillmentContext::new())
} else {
Box::new(FulfillmentContext::new_ignoring_regions())
}
}
}

/// Used if you want to have pleasant experience when dealing
/// with obligations outside of hir or mir typeck.
pub struct ObligationCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'a, 'tcx>,
engine: RefCell<Box<dyn TraitEngine<'tcx>>>,
}

impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
}

pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
}

pub fn register_obligations(
&self,
obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
) {
// Can't use `register_predicate_obligations` because the iterator
// may also use this `ObligationCtxt`.
for obligation in obligations {
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation)
}
}

pub fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
let InferOk { value, obligations } = infer_ok;
self.engine.borrow_mut().register_predicate_obligations(self.infcx, obligations);
value
}

/// Requires that `ty` must implement the trait with `def_id` in
/// the given environment. This trait must not have any type
/// parameters (except for `Self`).
pub fn register_bound(
&self,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
def_id: DefId,
) {
let tcx = self.infcx.tcx;
let trait_ref = ty::TraitRef { def_id, substs: tcx.mk_substs_trait(ty, &[]) };
self.register_obligation(Obligation {
cause,
recursion_depth: 0,
param_env,
predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
});
}

pub fn normalize<T: TypeFoldable<'tcx>>(
&self,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: T,
) -> T {
let infer_ok = self.infcx.partially_normalize_associated_types_in(cause, param_env, value);
self.register_infer_ok_obligations(infer_ok)
}

pub fn equate_types(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
) -> Result<(), TypeError<'tcx>> {
match self.infcx.at(cause, param_env).eq(expected, actual) {
Ok(InferOk { obligations, value: () }) => {
self.register_obligations(obligations);
Ok(())
}
Err(e) => Err(e),
}
}

pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> {
self.engine.borrow_mut().select_all_or_error(self.infcx)
}
}
40 changes: 24 additions & 16 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub use self::SelectionError::*;

pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
pub use self::coherence::{OrphanCheckErr, OverlapResult};
pub use self::engine::TraitEngineExt;
pub use self::engine::{ObligationCtxt, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::is_vtable_safe_method;
Expand Down Expand Up @@ -237,29 +237,37 @@ fn do_normalize_predicates<'tcx>(
// cares about declarations like `'a: 'b`.
let outlives_env = OutlivesEnvironment::new(elaborated_env);

infcx.resolve_regions_and_report_errors(&outlives_env);
// FIXME: It's very weird that we ignore region obligations but apparently
// still need to use `resolve_regions` as we need the resolved regions in
// the normalized predicates.
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() {
tcx.sess.delay_span_bug(
span,
format!(
"failed region resolution while normalizing {elaborated_env:?}: {errors:?}"
),
);
}

let predicates = match infcx.fully_resolve(predicates) {
Ok(predicates) => predicates,
match infcx.fully_resolve(predicates) {
Ok(predicates) => Ok(predicates),
Err(fixup_err) => {
// If we encounter a fixup error, it means that some type
// variable wound up unconstrained. I actually don't know
// if this can happen, and I certainly don't expect it to
// happen often, but if it did happen it probably
// represents a legitimate failure due to some kind of
// unconstrained variable, and it seems better not to ICE,
// all things considered.
let reported = tcx.sess.span_err(span, &fixup_err.to_string());
return Err(reported);
// unconstrained variable.
//
// @lcnr: Let's still ICE here for now. I want a test case
// for that.
span_bug!(
span,
"inference variables in normalized parameter environment: {}",
fixup_err
);
}
};
if predicates.needs_infer() {
let reported = tcx
.sess
.delay_span_bug(span, "encountered inference variables after `fully_resolve`");
Err(reported)
} else {
Ok(predicates)
}
})
}
Expand Down
Loading