normalize param-env type-outlives predicates last
The normalization of type-outlives predicates can depend on misc. environment predicates, but not the other way around. Inferred lifetime bounds can propagate type-outlives bounds far and wide, so their normalization needs to work well. Fixes #54467
This commit is contained in:
parent
f55129d003
commit
b95d0489d9
3 changed files with 172 additions and 57 deletions
|
@ -1456,7 +1456,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the selection, evaluation, and projection cachesThis is useful when
|
/// Clears the selection, evaluation, and projection caches. This is useful when
|
||||||
/// repeatedly attemping to select an Obligation while changing only
|
/// repeatedly attemping to select an Obligation while changing only
|
||||||
/// its ParamEnv, since FulfillmentContext doesn't use 'probe'
|
/// its ParamEnv, since FulfillmentContext doesn't use 'probe'
|
||||||
pub fn clear_caches(&self) {
|
pub fn clear_caches(&self) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ use ty::{self, AdtKind, List, Ty, TyCtxt, GenericParamDefKind, ToPredicate};
|
||||||
use ty::error::{ExpectedFound, TypeError};
|
use ty::error::{ExpectedFound, TypeError};
|
||||||
use ty::fold::{TypeFolder, TypeFoldable, TypeVisitor};
|
use ty::fold::{TypeFolder, TypeFoldable, TypeVisitor};
|
||||||
use infer::{InferCtxt};
|
use infer::{InferCtxt};
|
||||||
|
use util::common::ErrorReported;
|
||||||
|
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -632,44 +633,15 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this is gonna need to be removed ...
|
fn do_normalize_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
/// Normalizes the parameter environment, reporting errors if they occur.
|
region_context: DefId,
|
||||||
pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
cause: ObligationCause<'tcx>,
|
||||||
region_context: DefId,
|
elaborated_env: ty::ParamEnv<'tcx>,
|
||||||
unnormalized_env: ty::ParamEnv<'tcx>,
|
predicates: Vec<ty::Predicate<'tcx>>)
|
||||||
cause: ObligationCause<'tcx>)
|
-> Result<Vec<ty::Predicate<'tcx>>, ErrorReported>
|
||||||
-> ty::ParamEnv<'tcx>
|
|
||||||
{
|
{
|
||||||
// I'm not wild about reporting errors here; I'd prefer to
|
debug!("do_normalize_predicates({:?})", predicates);
|
||||||
// have the errors get reported at a defined place (e.g.,
|
|
||||||
// during typeck). Instead I have all parameter
|
|
||||||
// environments, in effect, going through this function
|
|
||||||
// and hence potentially reporting errors. This ensures of
|
|
||||||
// course that we never forget to normalize (the
|
|
||||||
// alternative seemed like it would involve a lot of
|
|
||||||
// manual invocations of this fn -- and then we'd have to
|
|
||||||
// deal with the errors at each of those sites).
|
|
||||||
//
|
|
||||||
// In any case, in practice, typeck constructs all the
|
|
||||||
// parameter environments once for every fn as it goes,
|
|
||||||
// and errors will get reported then; so after typeck we
|
|
||||||
// can be sure that no errors should occur.
|
|
||||||
|
|
||||||
let span = cause.span;
|
let span = cause.span;
|
||||||
|
|
||||||
debug!("normalize_param_env_or_error(unnormalized_env={:?})",
|
|
||||||
unnormalized_env);
|
|
||||||
|
|
||||||
let predicates: Vec<_> =
|
|
||||||
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
debug!("normalize_param_env_or_error: elaborated-predicates={:?}",
|
|
||||||
predicates);
|
|
||||||
|
|
||||||
let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
|
|
||||||
unnormalized_env.reveal);
|
|
||||||
|
|
||||||
tcx.infer_ctxt().enter(|infcx| {
|
tcx.infer_ctxt().enter(|infcx| {
|
||||||
// FIXME. We should really... do something with these region
|
// FIXME. We should really... do something with these region
|
||||||
// obligations. But this call just continues the older
|
// obligations. But this call just continues the older
|
||||||
|
@ -685,30 +657,21 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
// them here too, and we will remove this function when
|
// them here too, and we will remove this function when
|
||||||
// we move over to lazy normalization *anyway*.
|
// we move over to lazy normalization *anyway*.
|
||||||
let fulfill_cx = FulfillmentContext::new_ignoring_regions();
|
let fulfill_cx = FulfillmentContext::new_ignoring_regions();
|
||||||
|
|
||||||
let predicates = match fully_normalize(
|
let predicates = match fully_normalize(
|
||||||
&infcx,
|
&infcx,
|
||||||
fulfill_cx,
|
fulfill_cx,
|
||||||
cause,
|
cause,
|
||||||
elaborated_env,
|
elaborated_env,
|
||||||
// You would really want to pass infcx.param_env.caller_bounds here,
|
|
||||||
// but that is an interned slice, and fully_normalize takes &T and returns T, so
|
|
||||||
// without further refactoring, a slice can't be used. Luckily, we still have the
|
|
||||||
// predicate vector from which we created the ParamEnv in infcx, so we
|
|
||||||
// can pass that instead. It's roundabout and a bit brittle, but this code path
|
|
||||||
// ought to be refactored anyway, and until then it saves us from having to copy.
|
|
||||||
&predicates,
|
&predicates,
|
||||||
) {
|
) {
|
||||||
Ok(predicates) => predicates,
|
Ok(predicates) => predicates,
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
infcx.report_fulfillment_errors(&errors, None, false);
|
infcx.report_fulfillment_errors(&errors, None, false);
|
||||||
// An unnormalized env is better than nothing.
|
return Err(ErrorReported)
|
||||||
return elaborated_env;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("normalize_param_env_or_error: normalized predicates={:?}",
|
debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
|
||||||
predicates);
|
|
||||||
|
|
||||||
let region_scope_tree = region::ScopeTree::default();
|
let region_scope_tree = region::ScopeTree::default();
|
||||||
|
|
||||||
|
@ -734,21 +697,119 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
// unconstrained variable, and it seems better not to ICE,
|
// unconstrained variable, and it seems better not to ICE,
|
||||||
// all things considered.
|
// all things considered.
|
||||||
tcx.sess.span_err(span, &fixup_err.to_string());
|
tcx.sess.span_err(span, &fixup_err.to_string());
|
||||||
// An unnormalized env is better than nothing.
|
return Err(ErrorReported)
|
||||||
return elaborated_env;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let predicates = match tcx.lift_to_global(&predicates) {
|
match tcx.lift_to_global(&predicates) {
|
||||||
Some(predicates) => predicates,
|
Some(predicates) => Ok(predicates),
|
||||||
None => return elaborated_env,
|
None => {
|
||||||
|
// FIXME: shouldn't we, you know, actually report an error here? or an ICE?
|
||||||
|
Err(ErrorReported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this is gonna need to be removed ...
|
||||||
|
/// Normalizes the parameter environment, reporting errors if they occur.
|
||||||
|
pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
region_context: DefId,
|
||||||
|
unnormalized_env: ty::ParamEnv<'tcx>,
|
||||||
|
cause: ObligationCause<'tcx>)
|
||||||
|
-> ty::ParamEnv<'tcx>
|
||||||
|
{
|
||||||
|
// I'm not wild about reporting errors here; I'd prefer to
|
||||||
|
// have the errors get reported at a defined place (e.g.,
|
||||||
|
// during typeck). Instead I have all parameter
|
||||||
|
// environments, in effect, going through this function
|
||||||
|
// and hence potentially reporting errors. This ensures of
|
||||||
|
// course that we never forget to normalize (the
|
||||||
|
// alternative seemed like it would involve a lot of
|
||||||
|
// manual invocations of this fn -- and then we'd have to
|
||||||
|
// deal with the errors at each of those sites).
|
||||||
|
//
|
||||||
|
// In any case, in practice, typeck constructs all the
|
||||||
|
// parameter environments once for every fn as it goes,
|
||||||
|
// and errors will get reported then; so after typeck we
|
||||||
|
// can be sure that no errors should occur.
|
||||||
|
|
||||||
|
debug!("normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})",
|
||||||
|
region_context, unnormalized_env, cause);
|
||||||
|
|
||||||
|
let mut predicates: Vec<_> =
|
||||||
|
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
debug!("normalize_param_env_or_error: elaborated-predicates={:?}",
|
||||||
|
predicates);
|
||||||
|
|
||||||
|
let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
|
||||||
|
unnormalized_env.reveal);
|
||||||
|
|
||||||
|
// HACK: we are trying to normalize the param-env inside *itself*. The problem is that
|
||||||
|
// normalization expects its param-env to be already normalized, which means we have
|
||||||
|
// a circularity.
|
||||||
|
//
|
||||||
|
// The way we handle this is by normalizing the param-env inside an unnormalized version
|
||||||
|
// of the param-env, which means that if the param-env contains unnormalized projections,
|
||||||
|
// we'll have some normalization failures. This is unfortunate.
|
||||||
|
//
|
||||||
|
// Lazy normalization would basically handle this by treating just the
|
||||||
|
// normalizing-a-trait-ref-requires-itself cycles as evaluation failures.
|
||||||
|
//
|
||||||
|
// Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated
|
||||||
|
// types, so to make the situation less bad, we normalize all the predicates *but*
|
||||||
|
// the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
|
||||||
|
// then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
|
||||||
|
//
|
||||||
|
// This works fairly well because trait matching does not actually care about param-env
|
||||||
|
// TypeOutlives predicates - these are normally used by regionck.
|
||||||
|
let outlives_predicates: Vec<_> = predicates.drain_filter(|predicate| {
|
||||||
|
match predicate {
|
||||||
|
ty::Predicate::TypeOutlives(..) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
debug!("normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})",
|
||||||
|
predicates, outlives_predicates);
|
||||||
|
let non_outlives_predicates =
|
||||||
|
match do_normalize_predicates(tcx, region_context, cause.clone(),
|
||||||
|
elaborated_env, predicates) {
|
||||||
|
Ok(predicates) => predicates,
|
||||||
|
// An unnormalized env is better than nothing.
|
||||||
|
Err(ErrorReported) => {
|
||||||
|
debug!("normalize_param_env_or_error: errored resolving non-outlives predicates");
|
||||||
|
return elaborated_env
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("normalize_param_env_or_error: resolved predicates={:?}",
|
debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates);
|
||||||
predicates);
|
|
||||||
|
|
||||||
ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
|
// Not sure whether it is better to include the unnormalized TypeOutlives predicates
|
||||||
})
|
// here. I believe they should not matter, because we are ignoring TypeOutlives param-env
|
||||||
|
// predicates here anyway. Keeping them here anyway because it seems safer.
|
||||||
|
let outlives_env: Vec<_> =
|
||||||
|
non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect();
|
||||||
|
let outlives_env = ty::ParamEnv::new(tcx.intern_predicates(&outlives_env),
|
||||||
|
unnormalized_env.reveal);
|
||||||
|
let outlives_predicates =
|
||||||
|
match do_normalize_predicates(tcx, region_context, cause,
|
||||||
|
outlives_env, outlives_predicates) {
|
||||||
|
Ok(predicates) => predicates,
|
||||||
|
// An unnormalized env is better than nothing.
|
||||||
|
Err(ErrorReported) => {
|
||||||
|
debug!("normalize_param_env_or_error: errored resolving outlives predicates");
|
||||||
|
return elaborated_env
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates);
|
||||||
|
|
||||||
|
let mut predicates = non_outlives_predicates;
|
||||||
|
predicates.extend(outlives_predicates);
|
||||||
|
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
|
||||||
|
ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fully_normalize<'a, 'gcx, 'tcx, T>(
|
pub fn fully_normalize<'a, 'gcx, 'tcx, T>(
|
||||||
|
|
54
src/test/run-pass/issue-54467.rs
Normal file
54
src/test/run-pass/issue-54467.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
pub trait Stream {
|
||||||
|
type Item;
|
||||||
|
type Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ParseError<I> {
|
||||||
|
type Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseError<char> for u32 {
|
||||||
|
type Output = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for () {
|
||||||
|
type Item = char;
|
||||||
|
type Error = u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lex<'a, I>
|
||||||
|
where I: Stream,
|
||||||
|
I::Error: ParseError<char>,
|
||||||
|
<<I as Stream>::Error as ParseError<char>>::Output: 'a
|
||||||
|
{
|
||||||
|
x: &'a <I::Error as ParseError<char>>::Output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Reserved<'a, I> where
|
||||||
|
I: Stream<Item=char> + 'a,
|
||||||
|
I::Error: ParseError<I::Item>,
|
||||||
|
<<I as Stream>::Error as ParseError<char>>::Output: 'a
|
||||||
|
|
||||||
|
{
|
||||||
|
x: Lex<'a, I>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let r: Reserved<()> = Reserved {
|
||||||
|
x: Lex {
|
||||||
|
x: &()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let _v = r.x.x;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue