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

normalize param-env type-outlives predicates last #54701

Merged
merged 1 commit into from
Oct 2, 2018
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 src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// its ParamEnv, since FulfillmentContext doesn't use 'probe'
pub fn clear_caches(&self) {
Expand Down
173 changes: 117 additions & 56 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use ty::{self, AdtKind, List, Ty, TyCtxt, GenericParamDefKind, ToPredicate};
use ty::error::{ExpectedFound, TypeError};
use ty::fold::{TypeFolder, TypeFoldable, TypeVisitor};
use infer::{InferCtxt};
use util::common::ErrorReported;

use rustc_data_structures::sync::Lrc;
use std::fmt::Debug;
Expand Down Expand Up @@ -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 ...
/// 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>
fn do_normalize_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
region_context: DefId,
cause: ObligationCause<'tcx>,
elaborated_env: ty::ParamEnv<'tcx>,
predicates: Vec<ty::Predicate<'tcx>>)
-> Result<Vec<ty::Predicate<'tcx>>, ErrorReported>
{
// 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!("do_normalize_predicates({:?})", predicates);
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| {
// FIXME. We should really... do something with these region
// obligations. But this call just continues the older
Expand All @@ -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
// we move over to lazy normalization *anyway*.
let fulfill_cx = FulfillmentContext::new_ignoring_regions();

let predicates = match fully_normalize(
&infcx,
fulfill_cx,
cause,
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,
) {
Ok(predicates) => predicates,
Err(errors) => {
infcx.report_fulfillment_errors(&errors, None, false);
// An unnormalized env is better than nothing.
return elaborated_env;
return Err(ErrorReported)
}
};

debug!("normalize_param_env_or_error: normalized predicates={:?}",
predicates);
debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);

let region_scope_tree = region::ScopeTree::default();

Expand All @@ -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,
// all things considered.
tcx.sess.span_err(span, &fixup_err.to_string());
// An unnormalized env is better than nothing.
return elaborated_env;
return Err(ErrorReported)
}
};

let predicates = match tcx.lift_to_global(&predicates) {
Some(predicates) => predicates,
None => return elaborated_env,
match tcx.lift_to_global(&predicates) {
Some(predicates) => Ok(predicates),
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={:?}",
predicates);
debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates);

// 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);

ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
})
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>(
Expand Down
54 changes: 54 additions & 0 deletions src/test/run-pass/issue-54467.rs
Original file line number Diff line number Diff line change
@@ -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;
}