Merge pull request #26829 from nikomatsakis/better-object-defaults-warn-beta
Better object defaults warn beta
This commit is contained in:
commit
ebf0c83cb9
30 changed files with 481 additions and 90 deletions
|
|
@ -995,6 +995,42 @@ static mut FOO: Option<Box<usize>> = None;
|
|||
// error: mutable statics are not allowed to have destructors
|
||||
static mut BAR: Option<Vec<i32>> = None;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0398: r##"
|
||||
In Rust 1.3, the default object lifetime bounds are expected to
|
||||
change, as described in RFC #1156 [1]. You are getting a warning
|
||||
because the compiler thinks it is possible that this change will cause
|
||||
a compilation error in your code. It is possible, though unlikely,
|
||||
that this is a false alarm.
|
||||
|
||||
The heart of the change is that where `&'a Box<SomeTrait>` used to
|
||||
default to `&'a Box<SomeTrait+'a>`, it now defaults to `&'a
|
||||
Box<SomeTrait+'static>` (here, `SomeTrait` is the name of some trait
|
||||
type). Note that the only types which are affected are references to
|
||||
boxes, like `&Box<SomeTrait>` or `&[Box<SomeTrait>]`. More common
|
||||
types like `&SomeTrait` or `Box<SomeTrait>` are unaffected.
|
||||
|
||||
To silence this warning, edit your code to use an explicit bound.
|
||||
Most of the time, this means that you will want to change the
|
||||
signature of a function that you are calling. For example, if
|
||||
the error is reported on a call like `foo(x)`, and `foo` is
|
||||
defined as follows:
|
||||
|
||||
```
|
||||
fn foo(arg: &Box<SomeTrait>) { ... }
|
||||
```
|
||||
|
||||
you might change it to:
|
||||
|
||||
```
|
||||
fn foo<'a>(arg: &Box<SomeTrait+'a>) { ... }
|
||||
```
|
||||
|
||||
This explicitly states that you expect the trait object `SomeTrait` to
|
||||
contain references (with a maximum lifetime of `'a`).
|
||||
|
||||
[1]: https://github.com/rust-lang/rfcs/pull/1156
|
||||
"##
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -843,15 +843,15 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
|
|||
|
||||
fn parse_object_lifetime_default<'a,'tcx, F>(st: &mut PState<'a,'tcx>,
|
||||
conv: &mut F)
|
||||
-> Option<ty::ObjectLifetimeDefault>
|
||||
-> ty::ObjectLifetimeDefault
|
||||
where F: FnMut(DefIdSource, ast::DefId) -> ast::DefId,
|
||||
{
|
||||
match next(st) {
|
||||
'n' => None,
|
||||
'a' => Some(ty::ObjectLifetimeDefault::Ambiguous),
|
||||
'a' => ty::ObjectLifetimeDefault::Ambiguous,
|
||||
'b' => ty::ObjectLifetimeDefault::BaseDefault,
|
||||
's' => {
|
||||
let region = parse_region_(st, conv);
|
||||
Some(ty::ObjectLifetimeDefault::Specific(region))
|
||||
ty::ObjectLifetimeDefault::Specific(region)
|
||||
}
|
||||
_ => panic!("parse_object_lifetime_default: bad input")
|
||||
}
|
||||
|
|
@ -887,9 +887,16 @@ fn parse_existential_bounds_<'a,'tcx, F>(st: &mut PState<'a,'tcx>,
|
|||
}
|
||||
}
|
||||
|
||||
let region_bound_will_change = match next(st) {
|
||||
'y' => true,
|
||||
'n' => false,
|
||||
c => panic!("parse_ty: expected y/n not '{}'", c)
|
||||
};
|
||||
|
||||
return ty::ExistentialBounds { region_bound: region_bound,
|
||||
builtin_bounds: builtin_bounds,
|
||||
projection_bounds: projection_bounds };
|
||||
projection_bounds: projection_bounds,
|
||||
region_bound_will_change: region_bound_will_change };
|
||||
}
|
||||
|
||||
fn parse_builtin_bounds<F>(st: &mut PState, mut _conv: F) -> ty::BuiltinBounds where
|
||||
|
|
|
|||
|
|
@ -390,6 +390,8 @@ pub fn enc_existential_bounds<'a,'tcx>(w: &mut Encoder,
|
|||
}
|
||||
|
||||
mywrite!(w, ".");
|
||||
|
||||
mywrite!(w, "{}", if bs.region_bound_will_change {'y'} else {'n'});
|
||||
}
|
||||
|
||||
pub fn enc_region_bounds<'a, 'tcx>(w: &mut Encoder,
|
||||
|
|
@ -414,12 +416,12 @@ pub fn enc_type_param_def<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
|
|||
|
||||
fn enc_object_lifetime_default<'a, 'tcx>(w: &mut Encoder,
|
||||
cx: &ctxt<'a, 'tcx>,
|
||||
default: Option<ty::ObjectLifetimeDefault>)
|
||||
default: ty::ObjectLifetimeDefault)
|
||||
{
|
||||
match default {
|
||||
None => mywrite!(w, "n"),
|
||||
Some(ty::ObjectLifetimeDefault::Ambiguous) => mywrite!(w, "a"),
|
||||
Some(ty::ObjectLifetimeDefault::Specific(r)) => {
|
||||
ty::ObjectLifetimeDefault::Ambiguous => mywrite!(w, "a"),
|
||||
ty::ObjectLifetimeDefault::BaseDefault => mywrite!(w, "b"),
|
||||
ty::ObjectLifetimeDefault::Specific(r) => {
|
||||
mywrite!(w, "s");
|
||||
enc_region(w, cx, r);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Bivariate<'a, 'tcx> {
|
|||
|
||||
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||
|
||||
fn will_change(&mut self, _: bool, _: bool) -> bool {
|
||||
// since we are not comparing regions, we don't care
|
||||
false
|
||||
}
|
||||
|
||||
fn relate_with_variance<T:Relate<'a,'tcx>>(&mut self,
|
||||
variance: ty::Variance,
|
||||
a: &T,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ pub struct CombineFields<'a, 'tcx: 'a> {
|
|||
pub infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
pub a_is_expected: bool,
|
||||
pub trace: TypeTrace<'tcx>,
|
||||
pub cause: Option<ty_relate::Cause>,
|
||||
}
|
||||
|
||||
pub fn super_combine_tys<'a,'tcx:'a,R>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ impl<'a, 'tcx> TypeRelation<'a,'tcx> for Equate<'a, 'tcx> {
|
|||
|
||||
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||
|
||||
fn will_change(&mut self, a: bool, b: bool) -> bool {
|
||||
// if either side changed from what it was, that could cause equality to fail
|
||||
a || b
|
||||
}
|
||||
|
||||
fn relate_with_variance<T:Relate<'a,'tcx>>(&mut self,
|
||||
_: ty::Variance,
|
||||
a: &T,
|
||||
|
|
|
|||
|
|
@ -592,7 +592,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
sub: Region,
|
||||
sup: Region) {
|
||||
match origin {
|
||||
infer::Subtype(trace) => {
|
||||
infer::Subtype(trace) |
|
||||
infer::DefaultExistentialBound(trace) => {
|
||||
let terr = ty::terr_regions_does_not_outlive(sup, sub);
|
||||
self.report_and_explain_type_error(trace, &terr);
|
||||
}
|
||||
|
|
@ -1604,7 +1605,8 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
|
||||
fn note_region_origin(&self, origin: &SubregionOrigin<'tcx>) {
|
||||
match *origin {
|
||||
infer::Subtype(ref trace) => {
|
||||
infer::Subtype(ref trace) |
|
||||
infer::DefaultExistentialBound(ref trace) => {
|
||||
let desc = match trace.origin {
|
||||
infer::Misc(_) => {
|
||||
"types are compatible"
|
||||
|
|
|
|||
|
|
@ -35,6 +35,16 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Glb<'a, 'tcx> {
|
|||
|
||||
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||
|
||||
fn will_change(&mut self, a: bool, b: bool) -> bool {
|
||||
// Hmm, so the result of GLB will still be a LB if one or both
|
||||
// sides change to 'static, but it may no longer be the GLB.
|
||||
// I'm going to go with `a || b` here to be conservative,
|
||||
// since the result of this operation may be affected, though
|
||||
// I think it would mostly be more accepting than before (since the result
|
||||
// would be a bigger region).
|
||||
a || b
|
||||
}
|
||||
|
||||
fn relate_with_variance<T:Relate<'a,'tcx>>(&mut self,
|
||||
variance: ty::Variance,
|
||||
a: &T,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Lub<'a, 'tcx> {
|
|||
|
||||
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||
|
||||
fn will_change(&mut self, a: bool, b: bool) -> bool {
|
||||
// result will be 'static if a || b
|
||||
a || b
|
||||
}
|
||||
|
||||
fn relate_with_variance<T:Relate<'a,'tcx>>(&mut self,
|
||||
variance: ty::Variance,
|
||||
a: &T,
|
||||
|
|
|
|||
|
|
@ -173,6 +173,9 @@ pub enum SubregionOrigin<'tcx> {
|
|||
// Arose from a subtyping relation
|
||||
Subtype(TypeTrace<'tcx>),
|
||||
|
||||
// Arose from a subtyping relation
|
||||
DefaultExistentialBound(TypeTrace<'tcx>),
|
||||
|
||||
// Stack-allocated closures cannot outlive innermost loop
|
||||
// or function so as to ensure we only require finite stack
|
||||
InfStackClosure(Span),
|
||||
|
|
@ -472,7 +475,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
-> CombineFields<'a, 'tcx> {
|
||||
CombineFields {infcx: self,
|
||||
a_is_expected: a_is_expected,
|
||||
trace: trace}
|
||||
trace: trace,
|
||||
cause: None}
|
||||
}
|
||||
|
||||
// public so that it can be used from the rustc_driver unit tests
|
||||
|
|
@ -1125,6 +1129,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
|||
pub fn span(&self) -> Span {
|
||||
match *self {
|
||||
Subtype(ref a) => a.span(),
|
||||
DefaultExistentialBound(ref a) => a.span(),
|
||||
InfStackClosure(a) => a,
|
||||
InvokeClosure(a) => a,
|
||||
DerefPointer(a) => a,
|
||||
|
|
|
|||
|
|
@ -1358,9 +1358,56 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Check for future hostile edges tied to a bad default
|
||||
self.report_future_hostility(&graph);
|
||||
|
||||
(0..self.num_vars() as usize).map(|idx| var_data[idx].value).collect()
|
||||
}
|
||||
|
||||
fn report_future_hostility(&self, graph: &RegionGraph) {
|
||||
let constraints = self.constraints.borrow();
|
||||
for edge in graph.all_edges() {
|
||||
match constraints[&edge.data] {
|
||||
SubregionOrigin::DefaultExistentialBound(_) => {
|
||||
// this will become 'static in the future
|
||||
}
|
||||
_ => { continue; }
|
||||
}
|
||||
|
||||
// this constraint will become a 'static constraint in the
|
||||
// future, so walk outward and see if we have any hard
|
||||
// bounds that could not be inferred to 'static
|
||||
for nid in graph.depth_traverse(edge.target()) {
|
||||
for (_, succ) in graph.outgoing_edges(nid) {
|
||||
match succ.data {
|
||||
ConstrainVarSubReg(_, r) => {
|
||||
match r {
|
||||
ty::ReStatic | ty::ReInfer(_) => {
|
||||
/* OK */
|
||||
}
|
||||
ty::ReFree(_) | ty::ReScope(_) | ty::ReEmpty => {
|
||||
span_warn!(
|
||||
self.tcx.sess,
|
||||
constraints[&edge.data].span(),
|
||||
E0398,
|
||||
"this code may fail to compile in Rust 1.3 due to \
|
||||
the proposed change in object lifetime bound defaults");
|
||||
return; // only issue the warning once per fn
|
||||
}
|
||||
ty::ReEarlyBound(..) | ty::ReLateBound(..) => {
|
||||
self.tcx.sess.span_bug(
|
||||
constraints[&succ.data].span(),
|
||||
"relation to bound region");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_graph(&self) -> RegionGraph {
|
||||
let num_vars = self.num_vars();
|
||||
|
||||
|
|
|
|||
|
|
@ -10,16 +10,17 @@
|
|||
|
||||
use super::combine::{self, CombineFields};
|
||||
use super::higher_ranked::HigherRankedRelations;
|
||||
use super::Subtype;
|
||||
use super::SubregionOrigin;
|
||||
use super::type_variable::{SubtypeOf, SupertypeOf};
|
||||
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::ty::TyVar;
|
||||
use middle::ty_relate::{Relate, RelateResult, TypeRelation};
|
||||
use middle::ty_relate::{Cause, Relate, RelateResult, TypeRelation};
|
||||
use std::mem;
|
||||
|
||||
/// "Greatest lower bound" (common subtype)
|
||||
pub struct Sub<'a, 'tcx: 'a> {
|
||||
fields: CombineFields<'a, 'tcx>
|
||||
fields: CombineFields<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Sub<'a, 'tcx> {
|
||||
|
|
@ -33,6 +34,25 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> {
|
|||
fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.fields.infcx.tcx }
|
||||
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||
|
||||
fn with_cause<F,R>(&mut self, cause: Cause, f: F) -> R
|
||||
where F: FnOnce(&mut Self) -> R
|
||||
{
|
||||
debug!("sub with_cause={:?}", cause);
|
||||
let old_cause = mem::replace(&mut self.fields.cause, Some(cause));
|
||||
let r = f(self);
|
||||
debug!("sub old_cause={:?}", old_cause);
|
||||
self.fields.cause = old_cause;
|
||||
r
|
||||
}
|
||||
|
||||
fn will_change(&mut self, a: bool, b: bool) -> bool {
|
||||
// if we have (Foo+'a) <: (Foo+'b), this requires that 'a:'b.
|
||||
// So if 'a becomes 'static, no additional errors can occur.
|
||||
// OTOH, if 'a stays the same, but 'b becomes 'static, we
|
||||
// could have a problem.
|
||||
!a && b
|
||||
}
|
||||
|
||||
fn relate_with_variance<T:Relate<'a,'tcx>>(&mut self,
|
||||
variance: ty::Variance,
|
||||
a: &T,
|
||||
|
|
@ -84,11 +104,14 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn regions(&mut self, a: ty::Region, b: ty::Region) -> RelateResult<'tcx, ty::Region> {
|
||||
debug!("{}.regions({:?}, {:?})",
|
||||
self.tag(),
|
||||
a,
|
||||
b);
|
||||
let origin = Subtype(self.fields.trace.clone());
|
||||
debug!("{}.regions({:?}, {:?}) self.cause={:?}",
|
||||
self.tag(), a, b, self.fields.cause);
|
||||
let origin = match self.fields.cause {
|
||||
Some(Cause::ExistentialRegionBound(true)) =>
|
||||
SubregionOrigin::DefaultExistentialBound(self.fields.trace.clone()),
|
||||
_ =>
|
||||
SubregionOrigin::Subtype(self.fields.trace.clone()),
|
||||
};
|
||||
self.fields.infcx.region_vars.make_subregion(origin, a, b);
|
||||
Ok(a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2449,6 +2449,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
region_bound: data_b.bounds.region_bound,
|
||||
builtin_bounds: data_b.bounds.builtin_bounds,
|
||||
projection_bounds: data_a.bounds.projection_bounds.clone(),
|
||||
region_bound_will_change: data_b.bounds.region_bound_will_change,
|
||||
};
|
||||
|
||||
let new_trait = ty::mk_trait(tcx, data_a.principal.clone(), bounds);
|
||||
|
|
|
|||
|
|
@ -1854,6 +1854,11 @@ pub struct ExistentialBounds<'tcx> {
|
|||
pub region_bound: ty::Region,
|
||||
pub builtin_bounds: BuiltinBounds,
|
||||
pub projection_bounds: Vec<PolyProjectionPredicate<'tcx>>,
|
||||
|
||||
// If true, this TyTrait used a "default bound" in the surface
|
||||
// syntax. This makes no difference to the type system but is
|
||||
// handy for error reporting.
|
||||
pub region_bound_will_change: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
|
|
@ -1911,7 +1916,8 @@ pub enum BuiltinBound {
|
|||
pub fn region_existential_bound<'tcx>(r: ty::Region) -> ExistentialBounds<'tcx> {
|
||||
ty::ExistentialBounds { region_bound: r,
|
||||
builtin_bounds: BuiltinBounds::empty(),
|
||||
projection_bounds: Vec::new() }
|
||||
projection_bounds: Vec::new(),
|
||||
region_bound_will_change: false, }
|
||||
}
|
||||
|
||||
impl CLike for BuiltinBound {
|
||||
|
|
@ -2051,6 +2057,9 @@ pub enum ObjectLifetimeDefault {
|
|||
/// `T:'a` constraints are found.
|
||||
Ambiguous,
|
||||
|
||||
/// Use the base default, typically 'static, but in a fn body it is a fresh variable
|
||||
BaseDefault,
|
||||
|
||||
/// Use the given region as the default.
|
||||
Specific(Region),
|
||||
}
|
||||
|
|
@ -2062,7 +2071,7 @@ pub struct TypeParameterDef<'tcx> {
|
|||
pub space: subst::ParamSpace,
|
||||
pub index: u32,
|
||||
pub default: Option<Ty<'tcx>>,
|
||||
pub object_lifetime_default: Option<ObjectLifetimeDefault>,
|
||||
pub object_lifetime_default: ObjectLifetimeDefault,
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable, RustcDecodable, Clone, Debug)]
|
||||
|
|
@ -7764,6 +7773,7 @@ impl<'tcx> fmt::Debug for ObjectLifetimeDefault {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ObjectLifetimeDefault::Ambiguous => write!(f, "Ambiguous"),
|
||||
ObjectLifetimeDefault::BaseDefault => write!(f, "BaseDefault"),
|
||||
ObjectLifetimeDefault::Specific(ref r) => write!(f, "{:?}", r),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,6 +369,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault {
|
|||
ty::ObjectLifetimeDefault::Ambiguous =>
|
||||
ty::ObjectLifetimeDefault::Ambiguous,
|
||||
|
||||
ty::ObjectLifetimeDefault::BaseDefault =>
|
||||
ty::ObjectLifetimeDefault::BaseDefault,
|
||||
|
||||
ty::ObjectLifetimeDefault::Specific(r) =>
|
||||
ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)),
|
||||
}
|
||||
|
|
@ -725,6 +728,7 @@ pub fn super_fold_existential_bounds<'tcx, T: TypeFolder<'tcx>>(
|
|||
region_bound: bounds.region_bound.fold_with(this),
|
||||
builtin_bounds: bounds.builtin_bounds,
|
||||
projection_bounds: bounds.projection_bounds.fold_with(this),
|
||||
region_bound_will_change: bounds.region_bound_will_change,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Match<'a, 'tcx> {
|
|||
fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.tcx }
|
||||
fn a_is_expected(&self) -> bool { true } // irrelevant
|
||||
|
||||
fn will_change(&mut self, _: bool, _: bool) -> bool {
|
||||
// we're ignoring regions in this code
|
||||
false
|
||||
}
|
||||
|
||||
fn relate_with_variance<T:Relate<'a,'tcx>>(&mut self,
|
||||
_: ty::Variance,
|
||||
a: &T,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ use syntax::ast;
|
|||
|
||||
pub type RelateResult<'tcx, T> = Result<T, ty::type_err<'tcx>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Cause {
|
||||
ExistentialRegionBound(bool), // if true, this is a default, else explicit
|
||||
}
|
||||
|
||||
pub trait TypeRelation<'a,'tcx> : Sized {
|
||||
fn tcx(&self) -> &'a ty::ctxt<'tcx>;
|
||||
|
||||
|
|
@ -32,6 +37,19 @@ pub trait TypeRelation<'a,'tcx> : Sized {
|
|||
/// relation. Just affects error messages.
|
||||
fn a_is_expected(&self) -> bool;
|
||||
|
||||
fn with_cause<F,R>(&mut self, _cause: Cause, f: F) -> R
|
||||
where F: FnOnce(&mut Self) -> R
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
|
||||
/// Hack for deciding whether the lifetime bound defaults change
|
||||
/// will be a breaking change or not. The bools indicate whether
|
||||
/// `a`/`b` have a default that will change to `'static`; the
|
||||
/// result is true if this will potentially affect the affect of
|
||||
/// relating `a` and `b`.
|
||||
fn will_change(&mut self, a: bool, b: bool) -> bool;
|
||||
|
||||
/// Generic relation routine suitable for most anything.
|
||||
fn relate<T:Relate<'a,'tcx>>(&mut self, a: &T, b: &T) -> RelateResult<'tcx, T> {
|
||||
Relate::relate(self, a, b)
|
||||
|
|
@ -366,14 +384,21 @@ impl<'a,'tcx:'a> Relate<'a,'tcx> for ty::ExistentialBounds<'tcx> {
|
|||
-> RelateResult<'tcx, ty::ExistentialBounds<'tcx>>
|
||||
where R: TypeRelation<'a,'tcx>
|
||||
{
|
||||
let r = try!(relation.relate_with_variance(ty::Contravariant,
|
||||
&a.region_bound,
|
||||
&b.region_bound));
|
||||
let will_change = relation.will_change(a.region_bound_will_change,
|
||||
b.region_bound_will_change);
|
||||
|
||||
let r =
|
||||
try!(relation.with_cause(
|
||||
Cause::ExistentialRegionBound(will_change),
|
||||
|relation| relation.relate_with_variance(ty::Contravariant,
|
||||
&a.region_bound,
|
||||
&b.region_bound)));
|
||||
let nb = try!(relation.relate(&a.builtin_bounds, &b.builtin_bounds));
|
||||
let pb = try!(relation.relate(&a.projection_bounds, &b.projection_bounds));
|
||||
Ok(ty::ExistentialBounds { region_bound: r,
|
||||
builtin_bounds: nb,
|
||||
projection_bounds: pb })
|
||||
projection_bounds: pb,
|
||||
region_bound_will_change: will_change })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -291,13 +291,18 @@ impl<'tcx> fmt::Display for ty::TraitTy<'tcx> {
|
|||
try!(write!(f, " + {:?}", bound));
|
||||
}
|
||||
|
||||
// Region, if not obviously implied by builtin bounds.
|
||||
if bounds.region_bound != ty::ReStatic {
|
||||
// Region bound is implied by builtin bounds:
|
||||
let bound = bounds.region_bound.to_string();
|
||||
if !bound.is_empty() {
|
||||
try!(write!(f, " + {}", bound));
|
||||
}
|
||||
// FIXME: It'd be nice to compute from context when this bound
|
||||
// is implied, but that's non-trivial -- we'd perhaps have to
|
||||
// use thread-local data of some kind? There are also
|
||||
// advantages to just showing the region, since it makes
|
||||
// people aware that it's there.
|
||||
let bound = bounds.region_bound.to_string();
|
||||
if !bound.is_empty() {
|
||||
try!(write!(f, " + {}", bound));
|
||||
}
|
||||
|
||||
if bounds.region_bound_will_change && verbose() {
|
||||
try!(write!(f, " [WILL-CHANGE]"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -86,6 +86,16 @@ pub fn erase_regions<'tcx,T>(cx: &ty::ctxt<'tcx>, value: &T) -> T
|
|||
return t_norm;
|
||||
}
|
||||
|
||||
fn fold_existential_bounds(&mut self, s: &ty::ExistentialBounds<'tcx>)
|
||||
-> ty::ExistentialBounds<'tcx> {
|
||||
let mut s = ty_fold::super_fold_existential_bounds(self, s);
|
||||
|
||||
// this annoying flag messes up trans normalization
|
||||
s.region_bound_will_change = false;
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn fold_binder<T>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T>
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1538,7 +1538,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
|||
let rscope1 =
|
||||
&ObjectLifetimeDefaultRscope::new(
|
||||
rscope,
|
||||
Some(ty::ObjectLifetimeDefault::Specific(r)));
|
||||
ty::ObjectLifetimeDefault::Specific(r));
|
||||
let t = ast_ty_to_ty(this, rscope1, &*mt.ty);
|
||||
ty::mk_rptr(tcx, tcx.mk_region(r), ty::mt {ty: t, mutbl: mt.mutbl})
|
||||
}
|
||||
|
|
@ -2007,12 +2007,30 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx>(
|
|||
"only the builtin traits can be used as closure or object bounds");
|
||||
}
|
||||
|
||||
let region_bound = compute_object_lifetime_bound(this,
|
||||
rscope,
|
||||
span,
|
||||
®ion_bounds,
|
||||
principal_trait_ref,
|
||||
builtin_bounds);
|
||||
let region_bound =
|
||||
compute_object_lifetime_bound(this,
|
||||
span,
|
||||
®ion_bounds,
|
||||
principal_trait_ref,
|
||||
builtin_bounds);
|
||||
|
||||
let (region_bound, will_change) = match region_bound {
|
||||
Some(r) => (r, false),
|
||||
None => {
|
||||
match rscope.object_lifetime_default(span) {
|
||||
Some(r) => (r, rscope.object_lifetime_default_will_change_in_1_3()),
|
||||
None => {
|
||||
span_err!(this.tcx().sess, span, E0228,
|
||||
"the lifetime bound for this object type cannot be deduced \
|
||||
from context; please supply an explicit bound");
|
||||
(ty::ReStatic, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
debug!("region_bound: {:?} will_change: {:?}",
|
||||
region_bound, will_change);
|
||||
|
||||
ty::sort_bounds_list(&mut projection_bounds);
|
||||
|
||||
|
|
@ -2020,6 +2038,7 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx>(
|
|||
region_bound: region_bound,
|
||||
builtin_bounds: builtin_bounds,
|
||||
projection_bounds: projection_bounds,
|
||||
region_bound_will_change: will_change,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2029,12 +2048,11 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx>(
|
|||
/// region bounds. It may be that we can derive no bound at all, in which case we return `None`.
|
||||
fn compute_object_lifetime_bound<'tcx>(
|
||||
this: &AstConv<'tcx>,
|
||||
rscope: &RegionScope,
|
||||
span: Span,
|
||||
explicit_region_bounds: &[&ast::Lifetime],
|
||||
principal_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
builtin_bounds: ty::BuiltinBounds)
|
||||
-> ty::Region
|
||||
-> Option<ty::Region> // if None, use the default
|
||||
{
|
||||
let tcx = this.tcx();
|
||||
|
||||
|
|
@ -2052,11 +2070,11 @@ fn compute_object_lifetime_bound<'tcx>(
|
|||
if !explicit_region_bounds.is_empty() {
|
||||
// Explicitly specified region bound. Use that.
|
||||
let r = explicit_region_bounds[0];
|
||||
return ast_region_to_region(tcx, r);
|
||||
return Some(ast_region_to_region(tcx, r));
|
||||
}
|
||||
|
||||
if let Err(ErrorReported) = this.ensure_super_predicates(span,principal_trait_ref.def_id()) {
|
||||
return ty::ReStatic;
|
||||
return Some(ty::ReStatic);
|
||||
}
|
||||
|
||||
// No explicit region bound specified. Therefore, examine trait
|
||||
|
|
@ -2065,23 +2083,15 @@ fn compute_object_lifetime_bound<'tcx>(
|
|||
object_region_bounds(tcx, &principal_trait_ref, builtin_bounds);
|
||||
|
||||
// If there are no derived region bounds, then report back that we
|
||||
// can find no region bound.
|
||||
// can find no region bound. The caller will use the default.
|
||||
if derived_region_bounds.is_empty() {
|
||||
match rscope.object_lifetime_default(span) {
|
||||
Some(r) => { return r; }
|
||||
None => {
|
||||
span_err!(this.tcx().sess, span, E0228,
|
||||
"the lifetime bound for this object type cannot be deduced \
|
||||
from context; please supply an explicit bound");
|
||||
return ty::ReStatic;
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// If any of the derived region bounds are 'static, that is always
|
||||
// the best choice.
|
||||
if derived_region_bounds.iter().any(|r| ty::ReStatic == *r) {
|
||||
return ty::ReStatic;
|
||||
return Some(ty::ReStatic);
|
||||
}
|
||||
|
||||
// Determine whether there is exactly one unique region in the set
|
||||
|
|
@ -2092,7 +2102,7 @@ fn compute_object_lifetime_bound<'tcx>(
|
|||
span_err!(tcx.sess, span, E0227,
|
||||
"ambiguous lifetime bound, explicit lifetime bound required");
|
||||
}
|
||||
return r;
|
||||
return Some(r);
|
||||
}
|
||||
|
||||
pub struct PartitionedBounds<'a> {
|
||||
|
|
|
|||
|
|
@ -1876,6 +1876,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> {
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
Some(self.base_object_lifetime_default(span))
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, span: Span) -> ty::Region {
|
||||
// RFC #599 specifies that object lifetime defaults take
|
||||
// precedence over other defaults. But within a fn body we
|
||||
// don't have a *default* region, rather we use inference to
|
||||
|
|
@ -1883,7 +1887,7 @@ impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> {
|
|||
// (and anyway, within a fn body the right region may not even
|
||||
// be something the user can write explicitly, since it might
|
||||
// be some expression).
|
||||
Some(self.infcx().next_region_var(infer::MiscVariable(span)))
|
||||
self.infcx().next_region_var(infer::MiscVariable(span))
|
||||
}
|
||||
|
||||
fn anon_regions(&self, span: Span, count: usize)
|
||||
|
|
|
|||
|
|
@ -1535,8 +1535,7 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
|||
let object_lifetime_default_reprs: String =
|
||||
scheme.generics.types.iter()
|
||||
.map(|t| match t.object_lifetime_default {
|
||||
Some(ty::ObjectLifetimeDefault::Specific(r)) =>
|
||||
r.to_string(),
|
||||
ty::ObjectLifetimeDefault::Specific(r) => r.to_string(),
|
||||
d => format!("{:?}", d),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
|
|
@ -1640,7 +1639,7 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
|||
name: special_idents::type_self.name,
|
||||
def_id: local_def(param_id),
|
||||
default: None,
|
||||
object_lifetime_default: None,
|
||||
object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault,
|
||||
};
|
||||
|
||||
ccx.tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone());
|
||||
|
|
@ -1925,7 +1924,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
|||
param_id: ast::NodeId,
|
||||
param_bounds: &[ast::TyParamBound],
|
||||
where_clause: &ast::WhereClause)
|
||||
-> Option<ty::ObjectLifetimeDefault>
|
||||
-> ty::ObjectLifetimeDefault
|
||||
{
|
||||
let inline_bounds = from_bounds(ccx, param_bounds);
|
||||
let where_bounds = from_predicates(ccx, param_id, &where_clause.predicates);
|
||||
|
|
@ -1933,11 +1932,12 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
|||
.chain(where_bounds)
|
||||
.collect();
|
||||
return if all_bounds.len() > 1 {
|
||||
Some(ty::ObjectLifetimeDefault::Ambiguous)
|
||||
ty::ObjectLifetimeDefault::Ambiguous
|
||||
} else if all_bounds.len() == 0 {
|
||||
ty::ObjectLifetimeDefault::BaseDefault
|
||||
} else {
|
||||
all_bounds.into_iter()
|
||||
.next()
|
||||
.map(ty::ObjectLifetimeDefault::Specific)
|
||||
ty::ObjectLifetimeDefault::Specific(
|
||||
all_bounds.into_iter().next().unwrap())
|
||||
};
|
||||
|
||||
fn from_bounds<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,26 @@ pub trait RegionScope {
|
|||
/// be derived from the object traits, what should we use? If
|
||||
/// `None` is returned, an explicit annotation is required.
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region>;
|
||||
|
||||
/// The "base" default is the initial default for a scope. This is
|
||||
/// 'static except for in fn bodies, where it is a fresh inference
|
||||
/// variable. You shouldn't call this except for as part of
|
||||
/// computing `object_lifetime_default` (in particular, in legacy
|
||||
/// modes, it may not be relevant).
|
||||
fn base_object_lifetime_default(&self, span: Span) -> ty::Region;
|
||||
|
||||
/// Used to issue warnings in Rust 1.2, not needed after that.
|
||||
/// True if the result of `object_lifetime_default` will change in 1.3.
|
||||
fn object_lifetime_default_will_change_in_1_3(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Used to issue warnings in Rust 1.2, not needed after that.
|
||||
/// True if the result of `base_object_lifetime_default` differs
|
||||
/// from the result of `object_lifetime_default`.
|
||||
fn base_object_lifetime_default_differs(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// A scope in which all regions must be explicitly named. This is used
|
||||
|
|
@ -44,16 +64,20 @@ pub trait RegionScope {
|
|||
pub struct ExplicitRscope;
|
||||
|
||||
impl RegionScope for ExplicitRscope {
|
||||
fn object_lifetime_default(&self, _span: Span) -> Option<ty::Region> {
|
||||
Some(ty::ReStatic)
|
||||
}
|
||||
|
||||
fn anon_regions(&self,
|
||||
_span: Span,
|
||||
_count: usize)
|
||||
-> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>> {
|
||||
Err(None)
|
||||
}
|
||||
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
Some(self.base_object_lifetime_default(span))
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
|
||||
ty::ReStatic
|
||||
}
|
||||
}
|
||||
|
||||
// Same as `ExplicitRscope`, but provides some extra information for diagnostics
|
||||
|
|
@ -66,10 +90,6 @@ impl UnelidableRscope {
|
|||
}
|
||||
|
||||
impl RegionScope for UnelidableRscope {
|
||||
fn object_lifetime_default(&self, _span: Span) -> Option<ty::Region> {
|
||||
Some(ty::ReStatic)
|
||||
}
|
||||
|
||||
fn anon_regions(&self,
|
||||
_span: Span,
|
||||
_count: usize)
|
||||
|
|
@ -77,6 +97,14 @@ impl RegionScope for UnelidableRscope {
|
|||
let UnelidableRscope(ref v) = *self;
|
||||
Err(Some(v.clone()))
|
||||
}
|
||||
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
Some(self.base_object_lifetime_default(span))
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
|
||||
ty::ReStatic
|
||||
}
|
||||
}
|
||||
|
||||
// A scope in which omitted anonymous region defaults to
|
||||
|
|
@ -94,11 +122,15 @@ impl ElidableRscope {
|
|||
}
|
||||
|
||||
impl RegionScope for ElidableRscope {
|
||||
fn object_lifetime_default(&self, _span: Span) -> Option<ty::Region> {
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
// Per RFC #599, object-lifetimes default to 'static unless
|
||||
// overridden by context, and this takes precedence over
|
||||
// lifetime elision.
|
||||
Some(ty::ReStatic)
|
||||
Some(self.base_object_lifetime_default(span))
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
|
||||
ty::ReStatic
|
||||
}
|
||||
|
||||
fn anon_regions(&self,
|
||||
|
|
@ -131,11 +163,15 @@ impl BindingRscope {
|
|||
}
|
||||
|
||||
impl RegionScope for BindingRscope {
|
||||
fn object_lifetime_default(&self, _span: Span) -> Option<ty::Region> {
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
// Per RFC #599, object-lifetimes default to 'static unless
|
||||
// overridden by context, and this takes precedence over the
|
||||
// binding defaults.
|
||||
Some(ty::ReStatic)
|
||||
// binding defaults in a fn signature.
|
||||
Some(self.base_object_lifetime_default(span))
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
|
||||
ty::ReStatic
|
||||
}
|
||||
|
||||
fn anon_regions(&self,
|
||||
|
|
@ -150,12 +186,12 @@ impl RegionScope for BindingRscope {
|
|||
/// A scope which overrides the default object lifetime but has no other effect.
|
||||
pub struct ObjectLifetimeDefaultRscope<'r> {
|
||||
base_scope: &'r (RegionScope+'r),
|
||||
default: Option<ty::ObjectLifetimeDefault>,
|
||||
default: ty::ObjectLifetimeDefault,
|
||||
}
|
||||
|
||||
impl<'r> ObjectLifetimeDefaultRscope<'r> {
|
||||
pub fn new(base_scope: &'r (RegionScope+'r),
|
||||
default: Option<ty::ObjectLifetimeDefault>)
|
||||
default: ty::ObjectLifetimeDefault)
|
||||
-> ObjectLifetimeDefaultRscope<'r>
|
||||
{
|
||||
ObjectLifetimeDefaultRscope {
|
||||
|
|
@ -168,9 +204,49 @@ impl<'r> ObjectLifetimeDefaultRscope<'r> {
|
|||
impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> {
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
match self.default {
|
||||
None => self.base_scope.object_lifetime_default(span),
|
||||
Some(ty::ObjectLifetimeDefault::Ambiguous) => None,
|
||||
Some(ty::ObjectLifetimeDefault::Specific(r)) => Some(r),
|
||||
ty::ObjectLifetimeDefault::Ambiguous =>
|
||||
None,
|
||||
|
||||
ty::ObjectLifetimeDefault::BaseDefault =>
|
||||
if false { // this will become the behavior in Rust 1.3
|
||||
Some(self.base_object_lifetime_default(span))
|
||||
} else {
|
||||
self.base_scope.object_lifetime_default(span)
|
||||
},
|
||||
|
||||
ty::ObjectLifetimeDefault::Specific(r) =>
|
||||
Some(r),
|
||||
}
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, span: Span) -> ty::Region {
|
||||
assert!(false, "this code should not execute until Rust 1.3");
|
||||
self.base_scope.base_object_lifetime_default(span)
|
||||
}
|
||||
|
||||
fn object_lifetime_default_will_change_in_1_3(&self) -> bool {
|
||||
debug!("object_lifetime_default_will_change_in_1_3: {:?}", self.default);
|
||||
|
||||
match self.default {
|
||||
ty::ObjectLifetimeDefault::Ambiguous |
|
||||
ty::ObjectLifetimeDefault::Specific(_) =>
|
||||
false,
|
||||
|
||||
ty::ObjectLifetimeDefault::BaseDefault =>
|
||||
self.base_scope.base_object_lifetime_default_differs()
|
||||
}
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default_differs(&self) -> bool {
|
||||
debug!("base_object_lifetime_default_differs: {:?}", self.default);
|
||||
|
||||
match self.default {
|
||||
ty::ObjectLifetimeDefault::Ambiguous |
|
||||
ty::ObjectLifetimeDefault::Specific(_) =>
|
||||
true,
|
||||
|
||||
ty::ObjectLifetimeDefault::BaseDefault =>
|
||||
self.base_scope.base_object_lifetime_default_differs(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,6 +277,10 @@ impl<'r> RegionScope for ShiftedRscope<'r> {
|
|||
.map(|r| ty_fold::shift_region(r, 1))
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, span: Span) -> ty::Region {
|
||||
ty_fold::shift_region(self.base_scope.base_object_lifetime_default(span), 1)
|
||||
}
|
||||
|
||||
fn anon_regions(&self,
|
||||
span: Span,
|
||||
count: usize)
|
||||
|
|
|
|||
21
src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs
Normal file
21
src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
|
||||
// Helper for testing that we get suitable warnings when lifetime
|
||||
// bound change will cause breakage.
|
||||
|
||||
pub fn just_ref(x: &Fn()) {
|
||||
}
|
||||
|
||||
pub fn ref_obj(x: &Box<Fn()>) {
|
||||
// this will change to &Box<Fn()+'static>...
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
fn main() {
|
||||
static foo: Fn() -> u32 = || -> u32 {
|
||||
//~^ ERROR: mismatched types:
|
||||
//~| expected `core::ops::Fn() -> u32`,
|
||||
//~| expected `core::ops::Fn() -> u32 + 'static`,
|
||||
//~| found closure
|
||||
//~| (expected trait core::ops::Fn,
|
||||
//~| found closure)
|
||||
|
|
|
|||
64
src/test/compile-fail/lifetime-bound-will-change-warning.rs
Normal file
64
src/test/compile-fail/lifetime-bound-will-change-warning.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// 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.
|
||||
|
||||
// aux-build:lifetime_bound_will_change_warning_lib.rs
|
||||
|
||||
// Test that we get suitable warnings when lifetime bound change will
|
||||
// cause breakage.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
extern crate lifetime_bound_will_change_warning_lib as lib;
|
||||
|
||||
fn just_ref(x: &Fn()) {
|
||||
}
|
||||
|
||||
fn ref_obj(x: &Box<Fn()>) {
|
||||
// this will change to &Box<Fn()+'static>...
|
||||
|
||||
// Note: no warning is issued here, because the type of `x` will change to 'static
|
||||
if false { ref_obj(x); }
|
||||
}
|
||||
|
||||
fn test1<'a>(x: &'a Box<Fn()+'a>) {
|
||||
// just_ref will stay the same.
|
||||
just_ref(&**x)
|
||||
}
|
||||
|
||||
fn test1cc<'a>(x: &'a Box<Fn()+'a>) {
|
||||
// same as test1, but cross-crate
|
||||
lib::just_ref(&**x)
|
||||
}
|
||||
|
||||
fn test2<'a>(x: &'a Box<Fn()+'a>) {
|
||||
// but ref_obj will not, so warn.
|
||||
ref_obj(x) //~ WARNING this code may fail to compile in Rust 1.3
|
||||
}
|
||||
|
||||
fn test2cc<'a>(x: &'a Box<Fn()+'a>) {
|
||||
// same as test2, but cross crate
|
||||
lib::ref_obj(x) //~ WARNING this code may fail to compile in Rust 1.3
|
||||
}
|
||||
|
||||
fn test3<'a>(x: &'a Box<Fn()+'static>) {
|
||||
// here, we have a 'static bound, so even when ref_obj changes, no error results
|
||||
ref_obj(x)
|
||||
}
|
||||
|
||||
fn test3cc<'a>(x: &'a Box<Fn()+'static>) {
|
||||
// same as test3, but cross crate
|
||||
lib::ref_obj(x)
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { //~ ERROR compilation successful
|
||||
}
|
||||
|
|
@ -39,5 +39,9 @@ fn load1<'a,'b>(a: &'a MyBox<SomeTrait>,
|
|||
//~| ERROR mismatched types
|
||||
}
|
||||
|
||||
fn load2<'a>(ss: &MyBox<SomeTrait+'a>) -> MyBox<SomeTrait+'a> {
|
||||
load0(ss) //~ WARNING E0398
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_object_lifetime_default]
|
||||
struct A<T>(T); //~ ERROR None
|
||||
struct A<T>(T); //~ ERROR BaseDefault
|
||||
|
||||
#[rustc_object_lifetime_default]
|
||||
struct B<'a,T>(&'a (), T); //~ ERROR None
|
||||
struct B<'a,T>(&'a (), T); //~ ERROR BaseDefault
|
||||
|
||||
#[rustc_object_lifetime_default]
|
||||
struct C<'a,T:'a>(&'a T); //~ ERROR 'a
|
||||
|
|
@ -29,6 +29,6 @@ struct E<'a,'b:'a,T:'b>(&'a T, &'b T); //~ ERROR 'b
|
|||
struct F<'a,'b,T:'a,U:'b>(&'a T, &'b U); //~ ERROR 'a,'b
|
||||
|
||||
#[rustc_object_lifetime_default]
|
||||
struct G<'a,'b,T:'a,U:'a+'b>(&'a T, &'b U); //~ ERROR 'a,Some(Ambiguous)
|
||||
struct G<'a,'b,T:'a,U:'a+'b>(&'a T, &'b U); //~ ERROR 'a,Ambiguous
|
||||
|
||||
fn main() { }
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ fn c(x: Box<Foo+Sync+Send>) {
|
|||
|
||||
fn d(x: Box<Foo>) {
|
||||
a(x); //~ ERROR mismatched types
|
||||
//~| expected `Box<Foo + Send>`
|
||||
//~| found `Box<Foo>`
|
||||
//~| expected `Box<Foo + Send + 'static>`
|
||||
//~| found `Box<Foo + 'static>`
|
||||
//~| expected bounds `Send`
|
||||
//~| found no bounds
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,5 +26,5 @@ fn main() {
|
|||
std::intrinsics::type_name::<NT>(),
|
||||
// DST
|
||||
std::intrinsics::type_name::<DST>()
|
||||
)}, ("[u8]", "str", "core::marker::Copy", "NT", "DST"));
|
||||
)}, ("[u8]", "str", "core::marker::Copy + 'static", "NT", "DST"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue