Auto merge of #26370 - nikomatsakis:better-object-defaults-warn, r=nikomatsakis
This is an implementation of RFC rust-lang/rfcs#1156. It includes the code to implement the new rules, but that code is currently disabled. It also includes code to issue warnings when the change will cause breakage. These warnings try hard to be targeted but are also somewhat approximate. They could, with some effort, be made *more* targeted by adjusting the code in ty_relate that propagates the "will change" flag to consider the specific operation. Might be worth doing. r? @pnkfelix (I think you understand region inference best)
This commit is contained in:
commit
50c952b5b7
@ -1160,6 +1160,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,
|
||||
|
@ -593,7 +593,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);
|
||||
}
|
||||
@ -1569,7 +1570,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,
|
||||
|
@ -194,6 +194,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),
|
||||
@ -658,7 +661,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
|
||||
@ -1464,6 +1468,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)
|
||||
}
|
||||
|
@ -2445,6 +2445,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 = tcx.mk_trait(data_a.principal.clone(), bounds);
|
||||
|
@ -2055,6 +2055,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)]
|
||||
@ -2245,6 +2250,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),
|
||||
}
|
||||
@ -2256,7 +2264,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)]
|
||||
@ -7328,6 +7336,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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,13 +290,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(())
|
||||
|
@ -82,6 +82,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>
|
||||
{
|
||||
|
@ -1549,7 +1549,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);
|
||||
tcx.mk_ref(tcx.mk_region(r), ty::mt {ty: t, mutbl: mt.mutbl})
|
||||
}
|
||||
@ -2016,12 +2016,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);
|
||||
|
||||
@ -2029,6 +2047,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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2038,12 +2057,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();
|
||||
|
||||
@ -2061,11 +2079,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
|
||||
@ -2074,23 +2092,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
|
||||
@ -2101,7 +2111,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> {
|
||||
|
@ -1779,6 +1779,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
|
||||
@ -1786,7 +1790,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)
|
||||
|
@ -1532,8 +1532,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>>()
|
||||
@ -1637,7 +1636,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());
|
||||
@ -1928,7 +1927,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);
|
||||
@ -1936,11 +1935,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>,
|
||||
|
@ -45,6 +45,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
|
||||
@ -53,16 +73,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<ElisionFailureInfo>>> {
|
||||
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
|
||||
@ -75,10 +99,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)
|
||||
@ -86,6 +106,14 @@ impl RegionScope for UnelidableRscope {
|
||||
let UnelidableRscope(ref v) = *self;
|
||||
Err(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
|
||||
@ -103,11 +131,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,
|
||||
@ -140,11 +172,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,
|
||||
@ -159,12 +195,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 {
|
||||
@ -177,9 +213,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(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,6 +286,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…
x
Reference in New Issue
Block a user