Skip to content

Commit 52b2db1

Browse files
committed
warn if leak-check relies on LBRs that will change
When we do a "HR subtype" check, we replace all late-bound regions (LBR) in the subtype with fresh variables, and skolemize the late-bound regions in the supertype. If those skolemized regions from the supertype wind up being super-regions (directly or indirectly) of either - another skolemized region; or, - some region that pre-exists the HR subtype check - e.g., a region variable that is not one of those created to represent bound regions in the subtype then the subtype check fails. What will change when we fix #32330 is that some of the LBR in the subtype may become early-bound. In that case, they would no longer be in the "permitted set" of variables that can be related to a skolemized type. So the foundation for this warning is to collect variables that we found to be related to a skolemized type. For each of them, we have a `BoundRegion` which carries a `Issue32330` flag. We check whether any of those flags indicate that this variable was created from a lifetime that will change from late- to early-bound. If so, we issue a warning indicating that the results of compilation may change. This is imperfect, since there are other kinds of code that will not compile once #32330 is fixed. However, it fixes the errors observed in practice on crater runs.
1 parent 08034eb commit 52b2db1

File tree

5 files changed

+263
-1
lines changed

5 files changed

+263
-1
lines changed

src/librustc/infer/error_reporting.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ use hir::map as ast_map;
7777
use hir;
7878
use hir::print as pprust;
7979

80+
use lint;
8081
use hir::def::Def;
8182
use hir::def_id::DefId;
8283
use infer::{self, TypeOrigin};
@@ -1028,6 +1029,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10281029
let (fn_decl, generics) = rebuilder.rebuild();
10291030
self.give_expl_lifetime_param(err, &fn_decl, unsafety, constness, name, &generics, span);
10301031
}
1032+
1033+
pub fn issue_32330_warnings(&self, span: Span, issue32330s: &[ty::Issue32330]) {
1034+
for issue32330 in issue32330s {
1035+
match *issue32330 {
1036+
ty::Issue32330::WontChange => { }
1037+
ty::Issue32330::WillChange { fn_def_id, region_name } => {
1038+
self.tcx.sess.add_lint(
1039+
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
1040+
ast::CRATE_NODE_ID,
1041+
span,
1042+
format!("lifetime parameter `{0}` declared on fn `{1}` \
1043+
appears only in the return type, \
1044+
but here is required to be higher-ranked, \
1045+
which means that `{0}` must appear in both \
1046+
argument and return types",
1047+
region_name,
1048+
self.tcx.item_path_str(fn_def_id)));
1049+
}
1050+
}
1051+
}
1052+
}
10311053
}
10321054

10331055
struct RebuildPathInfo<'a> {
@@ -1939,3 +1961,4 @@ fn name_to_dummy_lifetime(name: ast::Name) -> hir::Lifetime {
19391961
span: codemap::DUMMY_SP,
19401962
name: name }
19411963
}
1964+

src/librustc/infer/higher_ranked/mod.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
use super::{CombinedSnapshot,
1515
InferCtxt,
16+
LateBoundRegion,
1617
HigherRankedType,
1718
SubregionOrigin,
1819
SkolemizationMap};
@@ -483,6 +484,43 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
483484
debug!("leak_check: skol_map={:?}",
484485
skol_map);
485486

487+
// ## Issue #32330 warnings
488+
//
489+
// When Issue #32330 is fixed, a certain number of late-bound
490+
// regions (LBR) will become early-bound. We wish to issue
491+
// warnings when the result of `leak_check` relies on such LBR, as
492+
// that means that compilation will likely start to fail.
493+
//
494+
// Recall that when we do a "HR subtype" check, we replace all
495+
// late-bound regions (LBR) in the subtype with fresh variables,
496+
// and skolemize the late-bound regions in the supertype. If those
497+
// skolemized regions from the supertype wind up being
498+
// super-regions (directly or indirectly) of either
499+
//
500+
// - another skolemized region; or,
501+
// - some region that pre-exists the HR subtype check
502+
// - e.g., a region variable that is not one of those created
503+
// to represent bound regions in the subtype
504+
//
505+
// then leak-check (and hence the subtype check) fails.
506+
//
507+
// What will change when we fix #32330 is that some of the LBR in the
508+
// subtype may become early-bound. In that case, they would no longer be in
509+
// the "permitted set" of variables that can be related to a skolemized
510+
// type.
511+
//
512+
// So the foundation for this warning is to collect variables that we found
513+
// to be related to a skolemized type. For each of them, we have a
514+
// `BoundRegion` which carries a `Issue32330` flag. We check whether any of
515+
// those flags indicate that this variable was created from a lifetime
516+
// that will change from late- to early-bound. If so, we issue a warning
517+
// indicating that the results of compilation may change.
518+
//
519+
// This is imperfect, since there are other kinds of code that will not
520+
// compile once #32330 is fixed. However, it fixes the errors observed in
521+
// practice on crater runs.
522+
let mut warnings = vec![];
523+
486524
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
487525
for (&skol_br, &skol) in skol_map {
488526
// The inputs to a skolemized variable can only
@@ -495,7 +533,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
495533
// or new variables:
496534
match tainted_region {
497535
ty::ReVar(vid) => {
498-
if new_vars.iter().any(|&x| x == vid) { continue; }
536+
if new_vars.contains(&vid) {
537+
warnings.extend(
538+
match self.region_vars.var_origin(vid) {
539+
LateBoundRegion(_,
540+
ty::BrNamed(_, _, wc),
541+
_) => Some(wc),
542+
_ => None,
543+
});
544+
continue;
545+
}
499546
}
500547
_ => {
501548
if tainted_region == skol { continue; }
@@ -519,6 +566,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
519566
}
520567
}
521568

569+
self.issue_32330_warnings(span, &warnings);
570+
522571
for (_, &skol) in skol_map {
523572
// The outputs from a skolemized variable must all be
524573
// equatable with `'static`.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2012 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+
#![feature(unboxed_closures)]
12+
#![feature(rustc_attrs)]
13+
14+
// Test for projection cache. We should be able to project distinct
15+
// lifetimes from `foo` as we reinstantiate it multiple times, but not
16+
// if we do it just once. In this variant, the region `'a` is used in
17+
// an contravariant position, which affects the results.
18+
19+
// revisions: ok oneuse transmute krisskross
20+
21+
#![allow(dead_code, unused_variables)]
22+
23+
fn foo<'a>() -> &'a u32 { loop { } }
24+
25+
fn bar<T>(t: T, x: T::Output) -> T::Output
26+
where T: FnOnce<()>
27+
{
28+
t()
29+
}
30+
31+
#[cfg(ok)] // two instantiations: OK
32+
fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
33+
let a = bar(foo, x);
34+
let b = bar(foo, y);
35+
(a, b)
36+
}
37+
38+
#[cfg(oneuse)] // one instantiation: OK (surprisingly)
39+
fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
40+
let f /* : fn() -> &'static u32 */ = foo; // <-- inferred type annotated
41+
let a = bar(f, x); // this is considered ok because fn args are contravariant...
42+
let b = bar(f, y); // ...and hence we infer T to distinct values in each call.
43+
(a, b)
44+
}
45+
46+
// FIXME(#32330)
47+
//#[cfg(transmute)] // one instantiations: BAD
48+
//fn baz<'a,'b>(x: &'a u32) -> &'static u32 {
49+
// bar(foo, x) //[transmute] ERROR E0495
50+
//}
51+
52+
// FIXME(#32330)
53+
//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
54+
//fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
55+
// let a = bar(foo, y); //[krisskross] ERROR E0495
56+
// let b = bar(foo, x); //[krisskross] ERROR E0495
57+
// (a, b)
58+
//}
59+
60+
#[rustc_error]
61+
fn main() { }
62+
//[ok]~^ ERROR compilation successful
63+
//[oneuse]~^^ ERROR compilation successful
64+
//[transmute]~^^^ ERROR compilation successful
65+
//[krisskross]~^^^^ ERROR compilation successful
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2012 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+
#![feature(unboxed_closures)]
12+
#![feature(rustc_attrs)]
13+
14+
// Test for projection cache. We should be able to project distinct
15+
// lifetimes from `foo` as we reinstantiate it multiple times, but not
16+
// if we do it just once. In this variant, the region `'a` is used in
17+
// an invariant position, which affects the results.
18+
19+
// revisions: ok oneuse transmute krisskross
20+
21+
#![allow(dead_code, unused_variables)]
22+
23+
use std::marker::PhantomData;
24+
25+
struct Type<'a> {
26+
// Invariant
27+
data: PhantomData<fn(&'a u32) -> &'a u32>
28+
}
29+
30+
fn foo<'a>() -> Type<'a> { loop { } }
31+
32+
fn bar<T>(t: T, x: T::Output) -> T::Output
33+
where T: FnOnce<()>
34+
{
35+
t()
36+
}
37+
38+
#[cfg(ok)] // two instantiations: OK
39+
fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
40+
let a = bar(foo, x);
41+
let b = bar(foo, y);
42+
(a, b)
43+
}
44+
45+
// FIXME(#32330)
46+
//#[cfg(oneuse)] // one instantiation: BAD
47+
//fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
48+
// let f = foo; // <-- No consistent type can be inferred for `f` here.
49+
// let a = bar(f, x); //[oneuse] ERROR E0495
50+
// let b = bar(f, y);
51+
// (a, b)
52+
//}
53+
54+
// FIXME(#32330)
55+
//#[cfg(transmute)] // one instantiations: BAD
56+
//fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
57+
// // Cannot instantiate `foo` with any lifetime other than `'a`,
58+
// // since it is provided as input.
59+
//
60+
// bar(foo, x) //[transmute] ERROR E0495
61+
//}
62+
63+
// FIXME(#32330)
64+
//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
65+
//fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
66+
// let a = bar(foo, y); //[krisskross] ERROR E0495
67+
// let b = bar(foo, x); //[krisskross] ERROR E0495
68+
// (a, b)
69+
//}
70+
71+
#[rustc_error]
72+
fn main() { }
73+
//[ok]~^ ERROR compilation successful
74+
//[oneuse]~^^ ERROR compilation successful
75+
//[transmute]~^^^ ERROR compilation successful
76+
//[krisskross]~^^^^ ERROR compilation successful
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2014 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+
// This test was derived from the wasm and parsell crates. They
12+
// stopped compiling when #32330 is fixed.
13+
14+
#![allow(dead_code, unused_variables)]
15+
#![deny(hr_lifetime_in_assoc_type)]
16+
#![feature(unboxed_closures)]
17+
18+
use std::str::Chars;
19+
20+
pub trait HasOutput<Ch, Str> {
21+
type Output;
22+
}
23+
24+
#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
25+
pub enum Token<'a> {
26+
Begin(&'a str)
27+
}
28+
29+
fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
30+
unimplemented!()
31+
}
32+
33+
fn foo<'a>(data: &mut Chars<'a>) {
34+
bar(mk_unexpected_char_err)
35+
//~^ ERROR lifetime parameter `'a` declared on fn `mk_unexpected_char_err`
36+
//~| WARNING hard error in a future release
37+
}
38+
39+
fn bar<F>(t: F)
40+
// No type can satisfy this requirement, since `'a` does not
41+
// appear in any of the input types:
42+
where F: for<'a> Fn() -> Option<&'a i32>
43+
//~^ ERROR associated type `Output` references lifetime `'a`, which does not
44+
//~| WARNING hard error in a future release
45+
{
46+
}
47+
48+
fn main() {
49+
}

0 commit comments

Comments
 (0)