Skip to content

Commit d6b010f

Browse files
committed
Auto merge of #46226 - arielb1:special-region-obligation, r=nikomatsakis
avoid type-live-for-region obligations on dummy nodes Type-live-for-region obligations on DUMMY_NODE_ID cause an ICE, and it turns out that in the few cases they are needed, these obligations are not needed anyway because they are verified elsewhere. Fixes #46069. Beta-nominating because this is a regression for our new beta. r? @nikomatsakis
2 parents 909b94b + ebd219a commit d6b010f

File tree

7 files changed

+148
-53
lines changed

7 files changed

+148
-53
lines changed

src/librustc/infer/outlives/obligations.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
8888
body_id: ast::NodeId,
8989
obligation: RegionObligation<'tcx>,
9090
) {
91+
debug!("register_region_obligation({:?}, {:?})", body_id, obligation);
9192
self.region_obligations
9293
.borrow_mut()
9394
.push((body_id, obligation));
@@ -180,18 +181,6 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
180181
TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env);
181182
outlives.type_must_outlive(origin, ty, region);
182183
}
183-
184-
/// Ignore the region obligations, not bothering to prove
185-
/// them. This function should not really exist; it is used to
186-
/// accommodate some older code for the time being.
187-
pub fn ignore_region_obligations(&self) {
188-
assert!(
189-
!self.in_snapshot.get(),
190-
"cannot ignore registered region obligations in a snapshot"
191-
);
192-
193-
self.region_obligations.borrow_mut().clear();
194-
}
195184
}
196185

197186
#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =)

src/librustc/traits/fulfill.rs

+47-17
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ pub struct FulfillmentContext<'tcx> {
4646
// A list of all obligations that have been registered with this
4747
// fulfillment context.
4848
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
49+
// Should this fulfillment context register type-lives-for-region
50+
// obligations on its parent infcx? In some cases, region
51+
// obligations are either already known to hold (normalization) or
52+
// hopefully verifed elsewhere (type-impls-bound), and therefore
53+
// should not be checked.
54+
//
55+
// Note that if we are normalizing a type that we already
56+
// know is well-formed, there should be no harm setting this
57+
// to true - all the region variables should be determinable
58+
// using the RFC 447 rules, which don't depend on
59+
// type-lives-for-region constraints, and because the type
60+
// is well-formed, the constraints should hold.
61+
register_region_obligations: bool,
4962
}
5063

5164
#[derive(Clone, Debug)]
@@ -59,6 +72,14 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
5972
pub fn new() -> FulfillmentContext<'tcx> {
6073
FulfillmentContext {
6174
predicates: ObligationForest::new(),
75+
register_region_obligations: true
76+
}
77+
}
78+
79+
pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
80+
FulfillmentContext {
81+
predicates: ObligationForest::new(),
82+
register_region_obligations: false
6283
}
6384
}
6485

@@ -191,7 +212,10 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
191212
debug!("select: starting another iteration");
192213

193214
// Process pending obligations.
194-
let outcome = self.predicates.process_obligations(&mut FulfillProcessor { selcx });
215+
let outcome = self.predicates.process_obligations(&mut FulfillProcessor {
216+
selcx,
217+
register_region_obligations: self.register_region_obligations
218+
});
195219
debug!("select: outcome={:?}", outcome);
196220

197221
// FIXME: if we kept the original cache key, we could mark projection
@@ -220,6 +244,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
220244

221245
struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
222246
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
247+
register_region_obligations: bool
223248
}
224249

225250
impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> {
@@ -230,7 +255,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
230255
obligation: &mut Self::Obligation)
231256
-> Result<Option<Vec<Self::Obligation>>, Self::Error>
232257
{
233-
process_predicate(self.selcx, obligation)
258+
process_predicate(self.selcx, obligation, self.register_region_obligations)
234259
.map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation {
235260
obligation: o,
236261
stalled_on: vec![]
@@ -269,7 +294,8 @@ fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 't
269294
/// - `Err` if the predicate does not hold
270295
fn process_predicate<'a, 'gcx, 'tcx>(
271296
selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
272-
pending_obligation: &mut PendingPredicateObligation<'tcx>)
297+
pending_obligation: &mut PendingPredicateObligation<'tcx>,
298+
register_region_obligations: bool)
273299
-> Result<Option<Vec<PredicateObligation<'tcx>>>,
274300
FulfillmentErrorCode<'tcx>>
275301
{
@@ -391,26 +417,30 @@ fn process_predicate<'a, 'gcx, 'tcx>(
391417
// `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`.
392418
Some(t_a) => {
393419
let r_static = selcx.tcx().types.re_static;
394-
selcx.infcx().register_region_obligation(
395-
obligation.cause.body_id,
396-
RegionObligation {
397-
sup_type: t_a,
398-
sub_region: r_static,
399-
cause: obligation.cause.clone(),
400-
});
420+
if register_region_obligations {
421+
selcx.infcx().register_region_obligation(
422+
obligation.cause.body_id,
423+
RegionObligation {
424+
sup_type: t_a,
425+
sub_region: r_static,
426+
cause: obligation.cause.clone(),
427+
});
428+
}
401429
Ok(Some(vec![]))
402430
}
403431
}
404432
}
405433
// If there aren't, register the obligation.
406434
Some(ty::OutlivesPredicate(t_a, r_b)) => {
407-
selcx.infcx().register_region_obligation(
408-
obligation.cause.body_id,
409-
RegionObligation {
410-
sup_type: t_a,
411-
sub_region: r_b,
412-
cause: obligation.cause.clone()
413-
});
435+
if register_region_obligations {
436+
selcx.infcx().register_region_obligation(
437+
obligation.cause.body_id,
438+
RegionObligation {
439+
sup_type: t_a,
440+
sub_region: r_b,
441+
cause: obligation.cause.clone()
442+
});
443+
}
414444
Ok(Some(vec![]))
415445
}
416446
}

src/librustc/traits/mod.rs

+35-16
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,10 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
431431
// this function's result remains infallible, we must confirm
432432
// that guess. While imperfect, I believe this is sound.
433433

434-
let mut fulfill_cx = FulfillmentContext::new();
434+
// The handling of regions in this area of the code is terrible,
435+
// see issue #29149. We should be able to improve on this with
436+
// NLL.
437+
let mut fulfill_cx = FulfillmentContext::new_ignoring_regions();
435438

436439
// We can use a dummy node-id here because we won't pay any mind
437440
// to region obligations that arise (there shouldn't really be any
@@ -511,8 +514,24 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
511514
unnormalized_env.reveal);
512515

513516
tcx.infer_ctxt().enter(|infcx| {
514-
let predicates = match fully_normalize(
517+
// FIXME. We should really... do something with these region
518+
// obligations. But this call just continues the older
519+
// behavior (i.e., doesn't cause any new bugs), and it would
520+
// take some further refactoring to actually solve them. In
521+
// particular, we would have to handle implied bounds
522+
// properly, and that code is currently largely confined to
523+
// regionck (though I made some efforts to extract it
524+
// out). -nmatsakis
525+
//
526+
// @arielby: In any case, these obligations are checked
527+
// by wfcheck anyway, so I'm not sure we have to check
528+
// them here too, and we will remove this function when
529+
// we move over to lazy normalization *anyway*.
530+
let fulfill_cx = FulfillmentContext::new_ignoring_regions();
531+
532+
let predicates = match fully_normalize_with_fulfillcx(
515533
&infcx,
534+
fulfill_cx,
516535
cause,
517536
elaborated_env,
518537
// You would really want to pass infcx.param_env.caller_bounds here,
@@ -537,16 +556,6 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
537556
let region_scope_tree = region::ScopeTree::default();
538557
let free_regions = FreeRegionMap::new();
539558

540-
// FIXME. We should really... do something with these region
541-
// obligations. But this call just continues the older
542-
// behavior (i.e., doesn't cause any new bugs), and it would
543-
// take some further refactoring to actually solve them. In
544-
// particular, we would have to handle implied bounds
545-
// properly, and that code is currently largely confined to
546-
// regionck (though I made some efforts to extract it
547-
// out). -nmatsakis
548-
let _ = infcx.ignore_region_obligations();
549-
550559
infcx.resolve_regions_and_report_errors(region_context, &region_scope_tree, &free_regions);
551560
let predicates = match infcx.fully_resolve(&predicates) {
552561
Ok(predicates) => predicates,
@@ -583,9 +592,6 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
583592
-> Result<T, Vec<FulfillmentError<'tcx>>>
584593
where T : TypeFoldable<'tcx>
585594
{
586-
debug!("fully_normalize(value={:?})", value);
587-
588-
let selcx = &mut SelectionContext::new(infcx);
589595
// FIXME (@jroesch) ISSUE 26721
590596
// I'm not sure if this is a bug or not, needs further investigation.
591597
// It appears that by reusing the fulfillment_cx here we incur more
@@ -599,8 +605,21 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
599605
//
600606
// I think we should probably land this refactor and then come
601607
// back to this is a follow-up patch.
602-
let mut fulfill_cx = FulfillmentContext::new();
608+
let fulfillcx = FulfillmentContext::new();
609+
fully_normalize_with_fulfillcx(infcx, fulfillcx, cause, param_env, value)
610+
}
603611

612+
pub fn fully_normalize_with_fulfillcx<'a, 'gcx, 'tcx, T>(
613+
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
614+
mut fulfill_cx: FulfillmentContext<'tcx>,
615+
cause: ObligationCause<'tcx>,
616+
param_env: ty::ParamEnv<'tcx>,
617+
value: &T)
618+
-> Result<T, Vec<FulfillmentError<'tcx>>>
619+
where T : TypeFoldable<'tcx>
620+
{
621+
debug!("fully_normalize_with_fulfillcx(value={:?})", value);
622+
let selcx = &mut SelectionContext::new(infcx);
604623
let Normalized { value: normalized_value, obligations } =
605624
project::normalize(selcx, param_env, cause, value);
606625
debug!("fully_normalize: normalized_value={:?} obligations={:?}",

src/librustc/traits/specialize/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,18 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
241241
// (which are packed up in penv)
242242

243243
infcx.save_and_restore_in_snapshot_flag(|infcx| {
244-
let mut fulfill_cx = FulfillmentContext::new();
244+
// If we came from `translate_substs`, we already know that the
245+
// predicates for our impl hold (after all, we know that a more
246+
// specialized impl holds, so our impl must hold too), and
247+
// we only want to process the projections to determine the
248+
// the types in our substs using RFC 447, so we can safely
249+
// ignore region obligations, which allows us to avoid threading
250+
// a node-id to assign them with.
251+
//
252+
// If we came from specialization graph construction, then
253+
// we already make a mockery out of the region system, so
254+
// why not ignore them a bit earlier?
255+
let mut fulfill_cx = FulfillmentContext::new_ignoring_regions();
245256
for oblig in obligations.into_iter() {
246257
fulfill_cx.register_predicate_obligation(&infcx, oblig);
247258
}

src/librustc/traits/structural_impls.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use traits;
1212
use traits::project::Normalized;
13-
use ty::{Lift, TyCtxt};
13+
use ty::{self, Lift, TyCtxt};
1414
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
1515

1616
use std::fmt;
@@ -28,9 +28,16 @@ impl<'tcx, T: fmt::Debug> fmt::Debug for Normalized<'tcx, T> {
2828

2929
impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> {
3030
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31-
write!(f, "Obligation(predicate={:?},depth={})",
32-
self.predicate,
33-
self.recursion_depth)
31+
if ty::tls::with(|tcx| tcx.sess.verbose()) {
32+
write!(f, "Obligation(predicate={:?},cause={:?},depth={})",
33+
self.predicate,
34+
self.cause,
35+
self.recursion_depth)
36+
} else {
37+
write!(f, "Obligation(predicate={:?},depth={})",
38+
self.predicate,
39+
self.recursion_depth)
40+
}
3441
}
3542
}
3643

src/librustc_mir/transform/nll/constraint_generation.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,17 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
152152
// associated types and parameters). We need to normalize
153153
// associated types here and possibly recursively process.
154154
for ty in dtorck_types {
155-
// FIXME -- I think that this may disregard some region obligations
156-
// or something. Do we care? -nmatsakis
157155
let cause = ObligationCause::dummy();
158-
match traits::fully_normalize(self.infcx, cause, self.param_env, &ty) {
156+
// We know that our original `dropped_ty` is well-formed,
157+
// so region obligations resulting from this normalization
158+
// should always hold.
159+
//
160+
// Therefore we ignore them instead of trying to match
161+
// them up with a location.
162+
let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
163+
match traits::fully_normalize_with_fulfillcx(
164+
self.infcx, fulfillcx, cause, self.param_env, &ty
165+
) {
159166
Ok(ty) => match ty.sty {
160167
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
161168
self.add_regular_live_constraint(ty, location);

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

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2017 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+
use std::iter::{Fuse, Cloned};
12+
use std::slice::Iter;
13+
14+
struct Foo<'a, T: 'a>(&'a T);
15+
impl<'a, T: 'a> Copy for Foo<'a, T> {}
16+
impl<'a, T: 'a> Clone for Foo<'a, T> {
17+
fn clone(&self) -> Self { *self }
18+
}
19+
20+
fn copy_ex() {
21+
let s = 2;
22+
let k1 = || s;
23+
let upvar = Foo(&k1);
24+
let k = || upvar;
25+
k();
26+
}
27+
28+
fn main() {
29+
let _f = 0 as *mut <Fuse<Cloned<Iter<u8>>> as Iterator>::Item;
30+
31+
copy_ex();
32+
}

0 commit comments

Comments
 (0)