Skip to content

Commit b95d048

Browse files
committed
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 rust-lang#54467
1 parent f55129d commit b95d048

File tree

3 files changed

+172
-57
lines changed

3 files changed

+172
-57
lines changed

src/librustc/infer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1456,7 +1456,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14561456
})
14571457
}
14581458

1459-
/// Clears the selection, evaluation, and projection cachesThis is useful when
1459+
/// Clears the selection, evaluation, and projection caches. This is useful when
14601460
/// repeatedly attemping to select an Obligation while changing only
14611461
/// its ParamEnv, since FulfillmentContext doesn't use 'probe'
14621462
pub fn clear_caches(&self) {

src/librustc/traits/mod.rs

+117-56
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use ty::{self, AdtKind, List, Ty, TyCtxt, GenericParamDefKind, ToPredicate};
2929
use ty::error::{ExpectedFound, TypeError};
3030
use ty::fold::{TypeFolder, TypeFoldable, TypeVisitor};
3131
use infer::{InferCtxt};
32+
use util::common::ErrorReported;
3233

3334
use rustc_data_structures::sync::Lrc;
3435
use std::fmt::Debug;
@@ -632,44 +633,15 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
632633
}
633634
}
634635

635-
// FIXME: this is gonna need to be removed ...
636-
/// Normalizes the parameter environment, reporting errors if they occur.
637-
pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
638-
region_context: DefId,
639-
unnormalized_env: ty::ParamEnv<'tcx>,
640-
cause: ObligationCause<'tcx>)
641-
-> ty::ParamEnv<'tcx>
636+
fn do_normalize_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
637+
region_context: DefId,
638+
cause: ObligationCause<'tcx>,
639+
elaborated_env: ty::ParamEnv<'tcx>,
640+
predicates: Vec<ty::Predicate<'tcx>>)
641+
-> Result<Vec<ty::Predicate<'tcx>>, ErrorReported>
642642
{
643-
// I'm not wild about reporting errors here; I'd prefer to
644-
// have the errors get reported at a defined place (e.g.,
645-
// during typeck). Instead I have all parameter
646-
// environments, in effect, going through this function
647-
// and hence potentially reporting errors. This ensures of
648-
// course that we never forget to normalize (the
649-
// alternative seemed like it would involve a lot of
650-
// manual invocations of this fn -- and then we'd have to
651-
// deal with the errors at each of those sites).
652-
//
653-
// In any case, in practice, typeck constructs all the
654-
// parameter environments once for every fn as it goes,
655-
// and errors will get reported then; so after typeck we
656-
// can be sure that no errors should occur.
657-
643+
debug!("do_normalize_predicates({:?})", predicates);
658644
let span = cause.span;
659-
660-
debug!("normalize_param_env_or_error(unnormalized_env={:?})",
661-
unnormalized_env);
662-
663-
let predicates: Vec<_> =
664-
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec())
665-
.collect();
666-
667-
debug!("normalize_param_env_or_error: elaborated-predicates={:?}",
668-
predicates);
669-
670-
let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
671-
unnormalized_env.reveal);
672-
673645
tcx.infer_ctxt().enter(|infcx| {
674646
// FIXME. We should really... do something with these region
675647
// 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>,
685657
// them here too, and we will remove this function when
686658
// we move over to lazy normalization *anyway*.
687659
let fulfill_cx = FulfillmentContext::new_ignoring_regions();
688-
689660
let predicates = match fully_normalize(
690661
&infcx,
691662
fulfill_cx,
692663
cause,
693664
elaborated_env,
694-
// You would really want to pass infcx.param_env.caller_bounds here,
695-
// but that is an interned slice, and fully_normalize takes &T and returns T, so
696-
// without further refactoring, a slice can't be used. Luckily, we still have the
697-
// predicate vector from which we created the ParamEnv in infcx, so we
698-
// can pass that instead. It's roundabout and a bit brittle, but this code path
699-
// ought to be refactored anyway, and until then it saves us from having to copy.
700665
&predicates,
701666
) {
702667
Ok(predicates) => predicates,
703668
Err(errors) => {
704669
infcx.report_fulfillment_errors(&errors, None, false);
705-
// An unnormalized env is better than nothing.
706-
return elaborated_env;
670+
return Err(ErrorReported)
707671
}
708672
};
709673

710-
debug!("normalize_param_env_or_error: normalized predicates={:?}",
711-
predicates);
674+
debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
712675

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

@@ -734,21 +697,119 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
734697
// unconstrained variable, and it seems better not to ICE,
735698
// all things considered.
736699
tcx.sess.span_err(span, &fixup_err.to_string());
737-
// An unnormalized env is better than nothing.
738-
return elaborated_env;
700+
return Err(ErrorReported)
739701
}
740702
};
741703

742-
let predicates = match tcx.lift_to_global(&predicates) {
743-
Some(predicates) => predicates,
744-
None => return elaborated_env,
704+
match tcx.lift_to_global(&predicates) {
705+
Some(predicates) => Ok(predicates),
706+
None => {
707+
// FIXME: shouldn't we, you know, actually report an error here? or an ICE?
708+
Err(ErrorReported)
709+
}
710+
}
711+
})
712+
}
713+
714+
// FIXME: this is gonna need to be removed ...
715+
/// Normalizes the parameter environment, reporting errors if they occur.
716+
pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
717+
region_context: DefId,
718+
unnormalized_env: ty::ParamEnv<'tcx>,
719+
cause: ObligationCause<'tcx>)
720+
-> ty::ParamEnv<'tcx>
721+
{
722+
// I'm not wild about reporting errors here; I'd prefer to
723+
// have the errors get reported at a defined place (e.g.,
724+
// during typeck). Instead I have all parameter
725+
// environments, in effect, going through this function
726+
// and hence potentially reporting errors. This ensures of
727+
// course that we never forget to normalize (the
728+
// alternative seemed like it would involve a lot of
729+
// manual invocations of this fn -- and then we'd have to
730+
// deal with the errors at each of those sites).
731+
//
732+
// In any case, in practice, typeck constructs all the
733+
// parameter environments once for every fn as it goes,
734+
// and errors will get reported then; so after typeck we
735+
// can be sure that no errors should occur.
736+
737+
debug!("normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})",
738+
region_context, unnormalized_env, cause);
739+
740+
let mut predicates: Vec<_> =
741+
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec())
742+
.collect();
743+
744+
debug!("normalize_param_env_or_error: elaborated-predicates={:?}",
745+
predicates);
746+
747+
let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
748+
unnormalized_env.reveal);
749+
750+
// HACK: we are trying to normalize the param-env inside *itself*. The problem is that
751+
// normalization expects its param-env to be already normalized, which means we have
752+
// a circularity.
753+
//
754+
// The way we handle this is by normalizing the param-env inside an unnormalized version
755+
// of the param-env, which means that if the param-env contains unnormalized projections,
756+
// we'll have some normalization failures. This is unfortunate.
757+
//
758+
// Lazy normalization would basically handle this by treating just the
759+
// normalizing-a-trait-ref-requires-itself cycles as evaluation failures.
760+
//
761+
// Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated
762+
// types, so to make the situation less bad, we normalize all the predicates *but*
763+
// the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
764+
// then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
765+
//
766+
// This works fairly well because trait matching does not actually care about param-env
767+
// TypeOutlives predicates - these are normally used by regionck.
768+
let outlives_predicates: Vec<_> = predicates.drain_filter(|predicate| {
769+
match predicate {
770+
ty::Predicate::TypeOutlives(..) => true,
771+
_ => false
772+
}
773+
}).collect();
774+
775+
debug!("normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})",
776+
predicates, outlives_predicates);
777+
let non_outlives_predicates =
778+
match do_normalize_predicates(tcx, region_context, cause.clone(),
779+
elaborated_env, predicates) {
780+
Ok(predicates) => predicates,
781+
// An unnormalized env is better than nothing.
782+
Err(ErrorReported) => {
783+
debug!("normalize_param_env_or_error: errored resolving non-outlives predicates");
784+
return elaborated_env
785+
}
745786
};
746787

747-
debug!("normalize_param_env_or_error: resolved predicates={:?}",
748-
predicates);
788+
debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates);
789+
790+
// Not sure whether it is better to include the unnormalized TypeOutlives predicates
791+
// here. I believe they should not matter, because we are ignoring TypeOutlives param-env
792+
// predicates here anyway. Keeping them here anyway because it seems safer.
793+
let outlives_env: Vec<_> =
794+
non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect();
795+
let outlives_env = ty::ParamEnv::new(tcx.intern_predicates(&outlives_env),
796+
unnormalized_env.reveal);
797+
let outlives_predicates =
798+
match do_normalize_predicates(tcx, region_context, cause,
799+
outlives_env, outlives_predicates) {
800+
Ok(predicates) => predicates,
801+
// An unnormalized env is better than nothing.
802+
Err(ErrorReported) => {
803+
debug!("normalize_param_env_or_error: errored resolving outlives predicates");
804+
return elaborated_env
805+
}
806+
};
807+
debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates);
749808

750-
ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
751-
})
809+
let mut predicates = non_outlives_predicates;
810+
predicates.extend(outlives_predicates);
811+
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
812+
ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
752813
}
753814

754815
pub fn fully_normalize<'a, 'gcx, 'tcx, T>(

src/test/run-pass/issue-54467.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub trait Stream {
12+
type Item;
13+
type Error;
14+
}
15+
16+
pub trait ParseError<I> {
17+
type Output;
18+
}
19+
20+
impl ParseError<char> for u32 {
21+
type Output = ();
22+
}
23+
24+
impl Stream for () {
25+
type Item = char;
26+
type Error = u32;
27+
}
28+
29+
pub struct Lex<'a, I>
30+
where I: Stream,
31+
I::Error: ParseError<char>,
32+
<<I as Stream>::Error as ParseError<char>>::Output: 'a
33+
{
34+
x: &'a <I::Error as ParseError<char>>::Output
35+
}
36+
37+
pub struct Reserved<'a, I> where
38+
I: Stream<Item=char> + 'a,
39+
I::Error: ParseError<I::Item>,
40+
<<I as Stream>::Error as ParseError<char>>::Output: 'a
41+
42+
{
43+
x: Lex<'a, I>
44+
}
45+
46+
fn main() {
47+
let r: Reserved<()> = Reserved {
48+
x: Lex {
49+
x: &()
50+
}
51+
};
52+
53+
let _v = r.x.x;
54+
}

0 commit comments

Comments
 (0)