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.
This commit is contained in:
parent
08034eb1a5
commit
52b2db1cca
@ -77,6 +77,7 @@
|
||||
use hir;
|
||||
use hir::print as pprust;
|
||||
|
||||
use lint;
|
||||
use hir::def::Def;
|
||||
use hir::def_id::DefId;
|
||||
use infer::{self, TypeOrigin};
|
||||
@ -1028,6 +1029,27 @@ fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegio
|
||||
let (fn_decl, generics) = rebuilder.rebuild();
|
||||
self.give_expl_lifetime_param(err, &fn_decl, unsafety, constness, name, &generics, span);
|
||||
}
|
||||
|
||||
pub fn issue_32330_warnings(&self, span: Span, issue32330s: &[ty::Issue32330]) {
|
||||
for issue32330 in issue32330s {
|
||||
match *issue32330 {
|
||||
ty::Issue32330::WontChange => { }
|
||||
ty::Issue32330::WillChange { fn_def_id, region_name } => {
|
||||
self.tcx.sess.add_lint(
|
||||
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
|
||||
ast::CRATE_NODE_ID,
|
||||
span,
|
||||
format!("lifetime parameter `{0}` declared on fn `{1}` \
|
||||
appears only in the return type, \
|
||||
but here is required to be higher-ranked, \
|
||||
which means that `{0}` must appear in both \
|
||||
argument and return types",
|
||||
region_name,
|
||||
self.tcx.item_path_str(fn_def_id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RebuildPathInfo<'a> {
|
||||
@ -1939,3 +1961,4 @@ fn name_to_dummy_lifetime(name: ast::Name) -> hir::Lifetime {
|
||||
span: codemap::DUMMY_SP,
|
||||
name: name }
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
use super::{CombinedSnapshot,
|
||||
InferCtxt,
|
||||
LateBoundRegion,
|
||||
HigherRankedType,
|
||||
SubregionOrigin,
|
||||
SkolemizationMap};
|
||||
@ -483,6 +484,43 @@ pub fn leak_check(&self,
|
||||
debug!("leak_check: skol_map={:?}",
|
||||
skol_map);
|
||||
|
||||
// ## Issue #32330 warnings
|
||||
//
|
||||
// When Issue #32330 is fixed, a certain number of late-bound
|
||||
// regions (LBR) will become early-bound. We wish to issue
|
||||
// warnings when the result of `leak_check` relies on such LBR, as
|
||||
// that means that compilation will likely start to fail.
|
||||
//
|
||||
// Recall that 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 leak-check (and hence 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.
|
||||
let mut warnings = vec![];
|
||||
|
||||
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
|
||||
for (&skol_br, &skol) in skol_map {
|
||||
// The inputs to a skolemized variable can only
|
||||
@ -495,7 +533,16 @@ pub fn leak_check(&self,
|
||||
// or new variables:
|
||||
match tainted_region {
|
||||
ty::ReVar(vid) => {
|
||||
if new_vars.iter().any(|&x| x == vid) { continue; }
|
||||
if new_vars.contains(&vid) {
|
||||
warnings.extend(
|
||||
match self.region_vars.var_origin(vid) {
|
||||
LateBoundRegion(_,
|
||||
ty::BrNamed(_, _, wc),
|
||||
_) => Some(wc),
|
||||
_ => None,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if tainted_region == skol { continue; }
|
||||
@ -519,6 +566,8 @@ pub fn leak_check(&self,
|
||||
}
|
||||
}
|
||||
|
||||
self.issue_32330_warnings(span, &warnings);
|
||||
|
||||
for (_, &skol) in skol_map {
|
||||
// The outputs from a skolemized variable must all be
|
||||
// equatable with `'static`.
|
||||
|
65
src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs
vendored
Normal file
65
src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
// Test for projection cache. We should be able to project distinct
|
||||
// lifetimes from `foo` as we reinstantiate it multiple times, but not
|
||||
// if we do it just once. In this variant, the region `'a` is used in
|
||||
// an contravariant position, which affects the results.
|
||||
|
||||
// revisions: ok oneuse transmute krisskross
|
||||
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
fn foo<'a>() -> &'a u32 { loop { } }
|
||||
|
||||
fn bar<T>(t: T, x: T::Output) -> T::Output
|
||||
where T: FnOnce<()>
|
||||
{
|
||||
t()
|
||||
}
|
||||
|
||||
#[cfg(ok)] // two instantiations: OK
|
||||
fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
|
||||
let a = bar(foo, x);
|
||||
let b = bar(foo, y);
|
||||
(a, b)
|
||||
}
|
||||
|
||||
#[cfg(oneuse)] // one instantiation: OK (surprisingly)
|
||||
fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
|
||||
let f /* : fn() -> &'static u32 */ = foo; // <-- inferred type annotated
|
||||
let a = bar(f, x); // this is considered ok because fn args are contravariant...
|
||||
let b = bar(f, y); // ...and hence we infer T to distinct values in each call.
|
||||
(a, b)
|
||||
}
|
||||
|
||||
// FIXME(#32330)
|
||||
//#[cfg(transmute)] // one instantiations: BAD
|
||||
//fn baz<'a,'b>(x: &'a u32) -> &'static u32 {
|
||||
// bar(foo, x) //[transmute] ERROR E0495
|
||||
//}
|
||||
|
||||
// FIXME(#32330)
|
||||
//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
|
||||
//fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
|
||||
// let a = bar(foo, y); //[krisskross] ERROR E0495
|
||||
// let b = bar(foo, x); //[krisskross] ERROR E0495
|
||||
// (a, b)
|
||||
//}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
||||
//[ok]~^ ERROR compilation successful
|
||||
//[oneuse]~^^ ERROR compilation successful
|
||||
//[transmute]~^^^ ERROR compilation successful
|
||||
//[krisskross]~^^^^ ERROR compilation successful
|
76
src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs
vendored
Normal file
76
src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
// Test for projection cache. We should be able to project distinct
|
||||
// lifetimes from `foo` as we reinstantiate it multiple times, but not
|
||||
// if we do it just once. In this variant, the region `'a` is used in
|
||||
// an invariant position, which affects the results.
|
||||
|
||||
// revisions: ok oneuse transmute krisskross
|
||||
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct Type<'a> {
|
||||
// Invariant
|
||||
data: PhantomData<fn(&'a u32) -> &'a u32>
|
||||
}
|
||||
|
||||
fn foo<'a>() -> Type<'a> { loop { } }
|
||||
|
||||
fn bar<T>(t: T, x: T::Output) -> T::Output
|
||||
where T: FnOnce<()>
|
||||
{
|
||||
t()
|
||||
}
|
||||
|
||||
#[cfg(ok)] // two instantiations: OK
|
||||
fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
|
||||
let a = bar(foo, x);
|
||||
let b = bar(foo, y);
|
||||
(a, b)
|
||||
}
|
||||
|
||||
// FIXME(#32330)
|
||||
//#[cfg(oneuse)] // one instantiation: BAD
|
||||
//fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
|
||||
// let f = foo; // <-- No consistent type can be inferred for `f` here.
|
||||
// let a = bar(f, x); //[oneuse] ERROR E0495
|
||||
// let b = bar(f, y);
|
||||
// (a, b)
|
||||
//}
|
||||
|
||||
// FIXME(#32330)
|
||||
//#[cfg(transmute)] // one instantiations: BAD
|
||||
//fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
|
||||
// // Cannot instantiate `foo` with any lifetime other than `'a`,
|
||||
// // since it is provided as input.
|
||||
//
|
||||
// bar(foo, x) //[transmute] ERROR E0495
|
||||
//}
|
||||
|
||||
// FIXME(#32330)
|
||||
//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
|
||||
//fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
|
||||
// let a = bar(foo, y); //[krisskross] ERROR E0495
|
||||
// let b = bar(foo, x); //[krisskross] ERROR E0495
|
||||
// (a, b)
|
||||
//}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
||||
//[ok]~^ ERROR compilation successful
|
||||
//[oneuse]~^^ ERROR compilation successful
|
||||
//[transmute]~^^^ ERROR compilation successful
|
||||
//[krisskross]~^^^^ ERROR compilation successful
|
49
src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs
vendored
Normal file
49
src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This test was derived from the wasm and parsell crates. They
|
||||
// stopped compiling when #32330 is fixed.
|
||||
|
||||
#![allow(dead_code, unused_variables)]
|
||||
#![deny(hr_lifetime_in_assoc_type)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
use std::str::Chars;
|
||||
|
||||
pub trait HasOutput<Ch, Str> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
|
||||
pub enum Token<'a> {
|
||||
Begin(&'a str)
|
||||
}
|
||||
|
||||
fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn foo<'a>(data: &mut Chars<'a>) {
|
||||
bar(mk_unexpected_char_err)
|
||||
//~^ ERROR lifetime parameter `'a` declared on fn `mk_unexpected_char_err`
|
||||
//~| WARNING hard error in a future release
|
||||
}
|
||||
|
||||
fn bar<F>(t: F)
|
||||
// No type can satisfy this requirement, since `'a` does not
|
||||
// appear in any of the input types:
|
||||
where F: for<'a> Fn() -> Option<&'a i32>
|
||||
//~^ ERROR associated type `Output` references lifetime `'a`, which does not
|
||||
//~| WARNING hard error in a future release
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
Loading…
Reference in New Issue
Block a user