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;
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>)
}
};
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
+ self.note_issue_32330(&mut diag, terr);
diag
}
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> {
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)
}
/// 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, ()>
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
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;
}
}
}
}
- self.issue_32330_warnings(span, &warnings);
-
Ok(())
}
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
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
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
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;
// `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.
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(|| {
}
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)
// 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;
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`
}
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)
}
}
///
/// 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),
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
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
}
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))
}
match *self {
- BrNamed(_, name, _) => write!(f, "{}", name),
+ BrNamed(_, name) => write!(f, "{}", name),
BrAnon(_) | BrFresh(_) | BrEnv => Ok(())
}
}
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),
}
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};
}
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)) => {
}
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
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>
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,
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.
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(),
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)
}
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))
};
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,
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<_>>();
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()
}
```
"##,
+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! {
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(..) |
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This test was derived from the wasm and parsell crates. They
+// stopped compiling when #32330 is fixed.
+
+#![allow(dead_code, unused_variables)]
+
+use std::str::Chars;
+
+pub trait HasOutput<Ch, Str> {
+ type Output;
+}
+
+#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
+pub enum Token<'a> {
+ Begin(&'a str)
+}
+
+fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
+ unimplemented!()
+}
+
+fn foo<'a>(data: &mut Chars<'a>) {
+ bar(mk_unexpected_char_err)
+}
+
+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 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
+{
+}
+
+fn main() {
+}
#![allow(dead_code)]
#![feature(rustc_attrs)]
-#![deny(hr_lifetime_in_assoc_type)]
+#![allow(hr_lifetime_in_assoc_type)]
trait Foo<'a> {
type Item;
// 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]
#![allow(dead_code)]
#![feature(rustc_attrs)]
#![feature(unboxed_closures)]
-#![deny(hr_lifetime_in_assoc_type)]
trait Foo {
type Item;
#[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)]
#[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)]
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// This test was derived from the wasm and parsell crates. They
-// stopped compiling when #32330 is fixed.
-
-#![allow(dead_code, unused_variables)]
-#![deny(hr_lifetime_in_assoc_type)]
-
-use std::str::Chars;
-
-pub trait HasOutput<Ch, Str> {
- type Output;
-}
-
-#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
-pub enum Token<'a> {
- Begin(&'a str)
-}
-
-fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
- unimplemented!()
-}
-
-fn foo<'a>(data: &mut Chars<'a>) {
- bar(mk_unexpected_char_err)
- //~^ ERROR lifetime parameter `'a` declared on fn `mk_unexpected_char_err`
- //~| WARNING hard error in a future release
-}
-
-fn bar<F>(t: F)
- // No type can satisfy this requirement, since `'a` does not
- // appear in any of the input types:
- where F: for<'a> Fn() -> Option<&'a i32>
- //~^ ERROR associated type `Output` references lifetime `'a`, which does not
- //~| WARNING hard error in a future release
-{
-}
-
-fn main() {
-}
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);
}
--- /dev/null
+// 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() {
+}
--- /dev/null
+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
+