Auto merge of #38897 - nikomatsakis:issue-32330-followup, r=arielb1

make lifetimes that only appear in return type early-bound

This is the full and proper fix for #32330. This also makes some effort to give a nice error message (as evidenced by the `ui` test), sending users over to the tracking issue for a fuller explanation and offering a `--explain` message in some cases.

This needs a crater run before we land.

r? @arielb1
This commit is contained in:
bors 2017-02-05 22:53:10 +00:00
commit a3da24bba9
20 changed files with 270 additions and 190 deletions

View File

@ -72,13 +72,12 @@
use hir::map as hir_map;
use hir;
use lint;
use hir::def_id::DefId;
use infer;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, TyCtxt, TypeFoldable};
use ty::{Region, ReFree};
use ty::{Region, ReFree, Issue32330};
use ty::error::TypeError;
use std::fmt;
@ -610,6 +609,39 @@ pub fn note_type_err(&self,
self.tcx.note_and_explain_type_err(diag, terr, span);
}
pub fn note_issue_32330(&self,
diag: &mut DiagnosticBuilder<'tcx>,
terr: &TypeError<'tcx>)
{
debug!("note_issue_32330: terr={:?}", terr);
match *terr {
TypeError::RegionsInsufficientlyPolymorphic(_, &Region::ReVar(vid)) |
TypeError::RegionsOverlyPolymorphic(_, &Region::ReVar(vid)) => {
match self.region_vars.var_origin(vid) {
RegionVariableOrigin::EarlyBoundRegion(_, _, Some(Issue32330 {
fn_def_id,
region_name
})) => {
diag.note(
&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)));
diag.note(
&format!("this error is the result of a recent bug fix; \
for more information, see issue #33685 \
<https://github.com/rust-lang/rust/issues/33685>"));
}
_ => { }
}
}
_ => { }
}
}
pub fn report_and_explain_type_error(&self,
trace: TypeTrace<'tcx>,
terr: &TypeError<'tcx>)
@ -629,6 +661,7 @@ pub fn report_and_explain_type_error(&self,
}
};
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
self.note_issue_32330(&mut diag, terr);
diag
}
@ -1053,27 +1086,6 @@ fn report_processed_errors(&self,
err.emit();
}
}
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)));
}
}
}
}
}
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
@ -1104,7 +1116,7 @@ fn report_inference_failure(&self,
format!(" for lifetime parameter {}in trait containing associated type `{}`",
br_string(br), type_name)
}
infer::EarlyBoundRegion(_, name) => {
infer::EarlyBoundRegion(_, name, _) => {
format!(" for lifetime parameter `{}`",
name)
}

View File

@ -622,7 +622,7 @@ pub fn skolemize_late_bound_regions<T>(&self,
/// hold. See `README.md` for more details.
pub fn leak_check(&self,
overly_polymorphic: bool,
span: Span,
_span: Span,
skol_map: &SkolemizationMap<'tcx>,
snapshot: &CombinedSnapshot)
-> RelateResult<'tcx, ()>
@ -630,43 +630,6 @@ 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
@ -680,13 +643,6 @@ pub fn leak_check(&self,
match *tainted_region {
ty::ReVar(vid) => {
if new_vars.contains(&vid) {
warnings.extend(
match self.region_vars.var_origin(vid) {
LateBoundRegion(_,
ty::BrNamed(.., wc),
_) => Some(wc),
_ => None,
});
continue;
}
}
@ -712,8 +668,6 @@ pub fn leak_check(&self,
}
}
self.issue_32330_warnings(span, &warnings);
Ok(())
}

View File

@ -368,7 +368,7 @@ pub enum RegionVariableOrigin {
Coercion(Span),
// Region variables created as the values for early-bound regions
EarlyBoundRegion(Span, ast::Name),
EarlyBoundRegion(Span, ast::Name, Option<ty::Issue32330>),
// Region variables created for bound regions
// in a function or method that is called
@ -1184,7 +1184,7 @@ pub fn region_var_for_def(&self,
span: Span,
def: &ty::RegionParameterDef)
-> &'tcx ty::Region {
self.next_region_var(EarlyBoundRegion(span, def.name))
self.next_region_var(EarlyBoundRegion(span, def.name, def.issue_32330))
}
/// Create a type inference variable for the given
@ -1761,7 +1761,7 @@ pub fn span(&self) -> Span {
AddrOfRegion(a) => a,
Autoref(a) => a,
Coercion(a) => a,
EarlyBoundRegion(a, _) => a,
EarlyBoundRegion(a, ..) => a,
LateBoundRegion(a, ..) => a,
BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP,
UpvarRegion(_, a) => a

View File

@ -31,7 +31,7 @@
use syntax::symbol::keywords;
use syntax_pos::Span;
use errors::DiagnosticBuilder;
use util::nodemap::{NodeMap, FxHashSet, FxHashMap, DefIdMap};
use util::nodemap::{NodeMap, NodeSet, FxHashSet, FxHashMap, DefIdMap};
use rustc_back::slice;
use hir;
@ -150,10 +150,14 @@ pub struct NamedRegionMap {
// `Region` describing how that region is bound
pub defs: NodeMap<Region>,
// the set of lifetime def ids that are late-bound; late-bound ids
// are named regions appearing in fn arguments that do not appear
// in where-clauses
pub late_bound: NodeMap<ty::Issue32330>,
// the set of lifetime def ids that are late-bound; a region can
// be late-bound if (a) it does NOT appear in a where-clause and
// (b) it DOES appear in the arguments.
pub late_bound: NodeSet,
// Contains the node-ids for lifetimes that were (incorrectly) categorized
// as late-bound, until #32330 was fixed.
pub issue_32330: NodeMap<ty::Issue32330>,
// For each type and trait definition, maps type parameters
// to the trait object lifetime defaults computed from them.
@ -261,7 +265,8 @@ pub fn krate(sess: &Session,
let krate = hir_map.krate();
let mut map = NamedRegionMap {
defs: NodeMap(),
late_bound: NodeMap(),
late_bound: NodeSet(),
issue_32330: NodeMap(),
object_lifetime_defaults: compute_object_lifetime_defaults(sess, hir_map),
};
sess.track_errors(|| {
@ -840,7 +845,7 @@ fn visit_early_late<F>(&mut self,
}
let lifetimes = generics.lifetimes.iter().map(|def| {
if self.map.late_bound.contains_key(&def.lifetime.id) {
if self.map.late_bound.contains(&def.lifetime.id) {
Region::late(def)
} else {
Region::early(&mut index, def)
@ -1610,22 +1615,26 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
// just mark it so we can issue warnings.
let constrained_by_input = constrained_by_input.regions.contains(&name);
let appears_in_output = appears_in_output.regions.contains(&name);
let will_change = !constrained_by_input && appears_in_output;
let issue_32330 = if will_change {
ty::Issue32330::WillChange {
fn_def_id: fn_def_id,
region_name: name,
}
} else {
ty::Issue32330::WontChange
};
if !constrained_by_input && appears_in_output {
debug!("inserting issue_32330 entry for {:?}, {:?} on {:?}",
lifetime.lifetime.id,
name,
fn_def_id);
map.issue_32330.insert(
lifetime.lifetime.id,
ty::Issue32330 {
fn_def_id: fn_def_id,
region_name: name,
});
continue;
}
debug!("insert_late_bound_lifetimes: \
lifetime {:?} with id {:?} is late-bound ({:?}",
lifetime.lifetime.name, lifetime.lifetime.id, issue_32330);
lifetime {:?} with id {:?} is late-bound",
lifetime.lifetime.name, lifetime.lifetime.id);
let prev = map.late_bound.insert(lifetime.lifetime.id, issue_32330);
assert!(prev.is_none(), "visited lifetime {:?} twice", lifetime.lifetime.id);
let inserted = map.late_bound.insert(lifetime.lifetime.id);
assert!(inserted, "visited lifetime {:?} twice", lifetime.lifetime.id);
}
return;

View File

@ -606,6 +606,7 @@ pub struct RegionParameterDef {
pub name: Name,
pub def_id: DefId,
pub index: u32,
pub issue_32330: Option<ty::Issue32330>,
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
/// on generic parameter `'a`, asserts data of lifetime `'a`
@ -622,8 +623,7 @@ pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion {
}
pub fn to_bound_region(&self) -> ty::BoundRegion {
// this is an early bound region, so unaffected by #32330
ty::BoundRegion::BrNamed(self.def_id, self.name, Issue32330::WontChange)
ty::BoundRegion::BrNamed(self.def_id, self.name)
}
}

View File

@ -58,7 +58,7 @@ pub enum BoundRegion {
///
/// The def-id is needed to distinguish free regions in
/// the event of shadowing.
BrNamed(DefId, Name, Issue32330),
BrNamed(DefId, Name),
/// Fresh bound identifiers created during GLB computations.
BrFresh(u32),
@ -68,23 +68,18 @@ pub enum BoundRegion {
BrEnv
}
/// True if this late-bound region is unconstrained, and hence will
/// become early-bound once #32330 is fixed.
/// When a region changed from late-bound to early-bound when #32330
/// was fixed, its `RegionParameterDef` will have one of these
/// structures that we can use to give nicer errors.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash,
RustcEncodable, RustcDecodable)]
pub enum Issue32330 {
WontChange,
pub struct Issue32330 {
/// fn where is region declared
pub fn_def_id: DefId,
/// this region will change from late-bound to early-bound once
/// #32330 is fixed.
WillChange {
/// fn where is region declared
fn_def_id: DefId,
/// name of region; duplicates the info in BrNamed but convenient
/// to have it here, and this code is only temporary
region_name: ast::Name,
}
/// name of region; duplicates the info in BrNamed but convenient
/// to have it here, and this code is only temporary
pub region_name: ast::Name,
}
// NB: If you change this, you'll probably want to change the corresponding

View File

@ -276,7 +276,7 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
let new_value = tcx.replace_late_bound_regions(&value, |br| {
let _ = start_or_continue(f, "for<", ", ");
let br = match br {
ty::BrNamed(_, name, _) => {
ty::BrNamed(_, name) => {
let _ = write!(f, "{}", name);
br
}
@ -286,8 +286,7 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
let name = Symbol::intern("'r");
let _ = write!(f, "{}", name);
ty::BrNamed(tcx.hir.local_def_id(CRATE_NODE_ID),
name,
ty::Issue32330::WontChange)
name)
}
};
tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), br))
@ -435,7 +434,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
}
match *self {
BrNamed(_, name, _) => write!(f, "{}", name),
BrNamed(_, name) => write!(f, "{}", name),
BrAnon(_) | BrFresh(_) | BrEnv => Ok(())
}
}
@ -446,9 +445,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BrAnon(n) => write!(f, "BrAnon({:?})", n),
BrFresh(n) => write!(f, "BrFresh({:?})", n),
BrNamed(did, name, issue32330) => {
write!(f, "BrNamed({:?}:{:?}, {:?}, {:?})",
did.krate, did.index, name, issue32330)
BrNamed(did, name) => {
write!(f, "BrNamed({:?}:{:?}, {:?})",
did.krate, did.index, name)
}
BrEnv => "BrEnv".fmt(f),
}

View File

@ -29,7 +29,6 @@
use hir::def::Def;
use hir::def_id::DefId;
use middle::resolve_lifetime as rl;
use rustc::lint;
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::traits;
use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
@ -148,20 +147,9 @@ pub fn ast_region_to_region(&self,
}
Some(&rl::Region::LateBound(debruijn, id)) => {
// If this region is declared on a function, it will have
// an entry in `late_bound`, but if it comes from
// `for<'a>` in some type or something, it won't
// necessarily have one. In that case though, we won't be
// changed from late to early bound, so we can just
// substitute false.
let issue_32330 = tcx.named_region_map
.late_bound
.get(&id)
.cloned()
.unwrap_or(ty::Issue32330::WontChange);
let name = tcx.hir.name(id);
tcx.mk_region(ty::ReLateBound(debruijn,
ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)))
ty::BrNamed(tcx.hir.local_def_id(id), name)))
}
Some(&rl::Region::LateBoundAnon(debruijn, index)) => {
@ -177,17 +165,10 @@ pub fn ast_region_to_region(&self,
}
Some(&rl::Region::Free(scope, id)) => {
// As in Region::LateBound above, could be missing for some late-bound
// regions, but also for early-bound regions.
let issue_32330 = tcx.named_region_map
.late_bound
.get(&id)
.cloned()
.unwrap_or(ty::Issue32330::WontChange);
let name = tcx.hir.name(id);
tcx.mk_region(ty::ReFree(ty::FreeRegion {
scope: scope.to_code_extent(&tcx.region_maps),
bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)
bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name)
}))
// (*) -- not late-bound, won't change
@ -566,7 +547,7 @@ fn trait_defines_associated_type_named(&self,
fn ast_type_binding_to_poly_projection_predicate(
&self,
path_id: ast::NodeId,
_path_id: ast::NodeId,
trait_ref: ty::PolyTraitRef<'tcx>,
binding: &ConvertedBinding<'tcx>)
-> Result<ty::PolyProjectionPredicate<'tcx>, ErrorReported>
@ -602,7 +583,7 @@ fn ast_type_binding_to_poly_projection_predicate(
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
let br_name = match *br {
ty::BrNamed(_, name, _) => name,
ty::BrNamed(_, name) => name,
_ => {
span_bug!(
binding.span,
@ -610,13 +591,13 @@ fn ast_type_binding_to_poly_projection_predicate(
br);
}
};
tcx.sess.add_lint(
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
path_id,
binding.span,
format!("binding for associated type `{}` references lifetime `{}`, \
which does not appear in the trait input types",
binding.item_name, br_name));
struct_span_err!(tcx.sess,
binding.span,
E0582,
"binding for associated type `{}` references lifetime `{}`, \
which does not appear in the trait input types",
binding.item_name, br_name)
.emit();
}
// Simple case: X is defined in the current trait.
@ -1197,7 +1178,7 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
for br in late_bound_in_ret.difference(&late_bound_in_args) {
let br_name = match *br {
ty::BrNamed(_, name, _) => name,
ty::BrNamed(_, name) => name,
_ => {
span_bug!(
bf.decl.output.span(),
@ -1205,13 +1186,13 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
br);
}
};
tcx.sess.add_lint(
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
ast_ty.id,
ast_ty.span,
format!("return type references lifetime `{}`, \
which does not appear in the trait input types",
br_name));
struct_span_err!(tcx.sess,
ast_ty.span,
E0581,
"return type references lifetime `{}`, \
which does not appear in the fn input types",
br_name)
.emit();
}
tcx.mk_fn_ptr(bare_fn_ty)
}

View File

@ -1418,7 +1418,7 @@ fn get_type_parameter_bounds(&self,
fn re_infer(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> Option<&'tcx ty::Region> {
let v = match def {
Some(def) => infer::EarlyBoundRegion(span, def.name),
Some(def) => infer::EarlyBoundRegion(span, def.name, def.issue_32330),
None => infer::MiscVariable(span)
};
Some(self.next_region_var(v))

View File

@ -108,7 +108,7 @@ fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>) -> WritebackCx<'cx, 'gcx, 'tcx> {
};
match *r {
ty::ReFree(ty::FreeRegion {
bound_region: ty::BoundRegion::BrNamed(def_id, name, _), ..
bound_region: ty::BoundRegion::BrNamed(def_id, name), ..
}) => {
let bound_region = gcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
index: i as u32,

View File

@ -1442,11 +1442,15 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let early_lifetimes = early_bound_lifetimes_from_generics(ccx, ast_generics);
let regions = early_lifetimes.iter().enumerate().map(|(i, l)| {
let issue_32330 = ccx.tcx.named_region_map.issue_32330
.get(&l.lifetime.id)
.cloned();
ty::RegionParameterDef {
name: l.lifetime.name,
index: own_start + i as u32,
def_id: tcx.hir.local_def_id(l.lifetime.id),
pure_wrt_drop: l.pure_wrt_drop,
issue_32330: issue_32330,
}
}).collect::<Vec<_>>();
@ -1675,7 +1679,7 @@ fn early_bound_lifetimes_from_generics<'a, 'tcx, 'hir>(
ast_generics
.lifetimes
.iter()
.filter(|l| !ccx.tcx.named_region_map.late_bound.contains_key(&l.lifetime.id))
.filter(|l| !ccx.tcx.named_region_map.late_bound.contains(&l.lifetime.id))
.collect()
}

View File

@ -4056,6 +4056,74 @@ fn main() {
```
"##,
E0581: r##"
In a `fn` type, a lifetime appears only in the return type,
and not in the arguments types.
Erroneous code example:
```compile_fail,E0581
fn main() {
// Here, `'a` appears only in the return type:
let x: for<'a> fn() -> &'a i32;
}
```
To fix this issue, either use the lifetime in the arguments, or use
`'static`. Example:
```
fn main() {
// Here, `'a` appears only in the return type:
let x: for<'a> fn(&'a i32) -> &'a i32;
let y: fn() -> &'static i32;
}
```
Note: The examples above used to be (erroneously) accepted by the
compiler, but this was since corrected. See [issue #33685] for more
details.
[issue #33685]: https://github.com/rust-lang/rust/issues/33685
"##,
E0582: r##"
A lifetime appears only in an associated-type binding,
and not in the input types to the trait.
Erroneous code example:
```compile_fail,E0582
fn bar<F>(t: F)
// No type can satisfy this requirement, since `'a` does not
// appear in any of the input types (here, `i32`):
where F: for<'a> Fn(i32) -> Option<&'a i32>
{
}
fn main() { }
```
To fix this issue, either use the lifetime in the inputs, or use
`'static`. Example:
```
fn bar<F, G>(t: F, u: G)
where F: for<'a> Fn(&'a i32) -> Option<&'a i32>,
G: Fn(i32) -> Option<&'static i32>,
{
}
fn main() { }
```
Note: The examples above used to be (erroneously) accepted by the
compiler, but this was since corrected. See [issue #33685] for more
details.
[issue #33685]: https://github.com/rust-lang/rust/issues/33685
"##,
}
register_diagnostics! {

View File

@ -810,7 +810,7 @@ impl Clean<Option<Lifetime>> for ty::Region {
fn clean(&self, cx: &DocContext) -> Option<Lifetime> {
match *self {
ty::ReStatic => Some(Lifetime::statik()),
ty::ReLateBound(_, ty::BrNamed(_, name, _)) => Some(Lifetime(name.to_string())),
ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(name.to_string())),
ty::ReEarlyBound(ref data) => Some(Lifetime(data.name.clean(cx))),
ty::ReLateBound(..) |

View File

@ -12,7 +12,6 @@
// stopped compiling when #32330 is fixed.
#![allow(dead_code, unused_variables)]
#![deny(hr_lifetime_in_assoc_type)]
use std::str::Chars;
@ -31,16 +30,21 @@ fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
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
//~^ ERROR E0582
{
}
fn baz<F>(t: F)
// No type can satisfy this requirement, since `'a` does not
// appear in any of the input types:
where F: for<'a> Iterator<Item=&'a i32>
//~^ ERROR E0582
{
}

View File

@ -12,7 +12,7 @@
#![allow(dead_code)]
#![feature(rustc_attrs)]
#![deny(hr_lifetime_in_assoc_type)]
#![allow(hr_lifetime_in_assoc_type)]
trait Foo<'a> {
type Item;
@ -25,40 +25,34 @@ impl<'a> Foo<'a> for() {
// Check that appearing in a projection input in the argument is not enough:
#[cfg(func)]
fn func1(_: for<'a> fn(<() as Foo<'a>>::Item) -> &'a i32) {
//[func]~^ ERROR return type references lifetime `'a`
//[func]~| WARNING previously accepted
//[func]~^ ERROR E0581
}
// Check that appearing in a projection input in the return still
// causes an error:
#[cfg(func)]
fn func2(_: for<'a> fn() -> <() as Foo<'a>>::Item) {
//[func]~^ ERROR return type references lifetime `'a`
//[func]~| WARNING previously accepted
//[func]~^ ERROR E0581
}
#[cfg(object)]
fn object1(_: Box<for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32>) {
//[object]~^ ERROR `Output` references lifetime `'a`
//[object]~| WARNING previously accepted
//[object]~^ ERROR E0582
}
#[cfg(object)]
fn object2(_: Box<for<'a> Fn() -> <() as Foo<'a>>::Item>) {
//[object]~^ ERROR `Output` references lifetime `'a`
//[object]~| WARNING previously accepted
//[object]~^ ERROR E0582
}
#[cfg(clause)]
fn clause1<T>() where T: for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32 {
//[clause]~^ ERROR `Output` references lifetime `'a`
//[clause]~| WARNING previously accepted
}
#[cfg(clause)]
fn clause2<T>() where T: for<'a> Fn() -> <() as Foo<'a>>::Item {
//[clause]~^ ERROR `Output` references lifetime `'a`
//[clause]~| WARNING previously accepted
}
#[rustc_error]

View File

@ -13,7 +13,6 @@
#![allow(dead_code)]
#![feature(rustc_attrs)]
#![feature(unboxed_closures)]
#![deny(hr_lifetime_in_assoc_type)]
trait Foo {
type Item;
@ -22,49 +21,41 @@ trait Foo {
#[cfg(angle)]
fn angle<T: for<'a> Foo<Item=&'a i32>>() {
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
//[angle]~| WARNING previously accepted
}
#[cfg(angle)]
fn angle1<T>() where T: for<'a> Foo<Item=&'a i32> {
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
//[angle]~| WARNING previously accepted
}
#[cfg(angle)]
fn angle2<T>() where for<'a> T: Foo<Item=&'a i32> {
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
//[angle]~| WARNING previously accepted
}
#[cfg(angle)]
fn angle3(_: &for<'a> Foo<Item=&'a i32>) {
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
//[angle]~| WARNING previously accepted
}
#[cfg(paren)]
fn paren<T: for<'a> Fn() -> &'a i32>() {
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
//[paren]~| WARNING previously accepted
}
#[cfg(paren)]
fn paren1<T>() where T: for<'a> Fn() -> &'a i32 {
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
//[paren]~| WARNING previously accepted
}
#[cfg(paren)]
fn paren2<T>() where for<'a> T: Fn() -> &'a i32 {
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
//[paren]~| WARNING previously accepted
}
#[cfg(paren)]
fn paren3(_: &for<'a> Fn() -> &'a i32) {
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
//[paren]~| WARNING previously accepted
}
#[cfg(elision)]

View File

@ -22,27 +22,23 @@ trait Foo {
#[cfg(sig)]
fn sig1(_: for<'a> fn() -> &'a i32) {
//[sig]~^ ERROR return type references lifetime `'a`
//[sig]~| WARNING previously accepted
}
#[cfg(sig)]
fn sig2(_: for<'a, 'b> fn(&'b i32) -> &'a i32) {
//[sig]~^ ERROR return type references lifetime `'a`
//[sig]~| WARNING previously accepted
}
#[cfg(local)]
fn local1() {
let _: for<'a> fn() -> &'a i32 = loop { };
//[local]~^ ERROR return type references lifetime `'a`
//[local]~| WARNING previously accepted
}
#[cfg(structure)]
struct Struct1 {
x: for<'a> fn() -> &'a i32
//[structure]~^ ERROR return type references lifetime `'a`
//[structure]~| WARNING previously accepted
}
#[cfg(elision)]

View File

@ -46,7 +46,10 @@ fn baz(x: &S) -> &S {
fn supply_F() {
want_F(foo);
want_F(bar);
// FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
want_F(bar); //~ ERROR E0308
want_F(baz);
}

View File

@ -0,0 +1,57 @@
// 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.
// In this fn, the type `F` is a function that takes a reference to a
// struct and returns another reference with the same lifetime.
//
// Meanwhile, the bare fn `foo` takes a reference to a struct with
// *ANY* lifetime and returns a reference with the 'static lifetime.
// This can safely be considered to be an instance of `F` because all
// lifetimes are sublifetimes of 'static.
#![allow(dead_code)]
#![allow(unused_variables)]
struct S;
// Given 'cx, return 'cx
type F = for<'cx> fn(&'cx S) -> &'cx S;
fn want_F(f: F) { }
// Given anything, return 'static
type G = for<'cx> fn(&'cx S) -> &'static S;
fn want_G(f: G) { }
// Should meet both.
fn foo(x: &S) -> &'static S {
panic!()
}
// Should meet both.
fn bar<'a,'b>(x: &'a S) -> &'b S {
panic!()
}
// Meets F, but not G.
fn baz(x: &S) -> &S {
panic!()
}
fn supply_F() {
want_F(foo);
// FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
want_F(bar); //~ ERROR E0308
want_F(baz);
}
pub fn main() {
}

View File

@ -0,0 +1,13 @@
error[E0308]: mismatched types
--> $DIR/regions-fn-subtyping-return-static.rs:51:12
|
51 | want_F(bar); //~ ERROR E0308
| ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
|
= note: expected type `fn(&'cx S) -> &'cx S`
found type `fn(&'a S) -> &S {bar::<'_>}`
= note: lifetime parameter `'b` declared on fn `bar` appears only in the return type, but here is required to be higher-ranked, which means that `'b` must appear in both argument and return types
= note: this error is the result of a recent bug fix; for more information, see issue #33685 <https://github.com/rust-lang/rust/issues/33685>
error: aborting due to previous error