diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 6dd98425df31..d7971cd2cf04 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -167,6 +167,13 @@ declare_lint! { "transmute from function item type to pointer-sized type erroneously allowed" } +declare_lint! { + pub HR_LIFETIME_IN_ASSOC_TYPE, + Warn, + "binding for associated type references higher-ranked lifetime \ + that does not appear in the trait input types" +} + declare_lint! { pub OVERLAPPING_INHERENT_IMPLS, Warn, @@ -234,7 +241,8 @@ impl LintPass for HardwiredLints { RENAMED_AND_REMOVED_LINTS, SUPER_OR_SELF_IN_GLOBAL_PATH, UNSIZED_IN_TUPLE, - OBJECT_UNSAFE_FRAGMENT + OBJECT_UNSAFE_FRAGMENT, + HR_LIFETIME_IN_ASSOC_TYPE ) } } diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 14b369f244d4..4a14185b6e3a 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -382,6 +382,35 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + /// Returns a set of all late-bound regions that are constrained + /// by `value`, meaning that if we instantiate those LBR with + /// variables and equate `value` with something else, those + /// variables will also be equated. + pub fn collect_constrained_late_bound_regions(&self, value: &Binder) + -> FnvHashSet + where T : TypeFoldable<'tcx> + { + self.collect_late_bound_regions(value, true) + } + + /// Returns a set of all late-bound regions that appear in `value` anywhere. + pub fn collect_referenced_late_bound_regions(&self, value: &Binder) + -> FnvHashSet + where T : TypeFoldable<'tcx> + { + self.collect_late_bound_regions(value, false) + } + + fn collect_late_bound_regions(&self, value: &Binder, just_constraint: bool) + -> FnvHashSet + where T : TypeFoldable<'tcx> + { + let mut collector = LateBoundRegionsCollector::new(just_constraint); + let result = value.skip_binder().visit_with(&mut collector); + assert!(!result); // should never have stopped early + collector.regions + } + /// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also /// method lookup and a few other places where precise region relationships are not required. pub fn erase_late_bound_regions(self, value: &Binder) -> T @@ -625,3 +654,54 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { false } } + +/// Collects all the late-bound regions it finds into a hash set. +struct LateBoundRegionsCollector { + current_depth: u32, + regions: FnvHashSet, + just_constrained: bool, +} + +impl LateBoundRegionsCollector { + fn new(just_constrained: bool) -> Self { + LateBoundRegionsCollector { + current_depth: 1, + regions: FnvHashSet(), + just_constrained: just_constrained, + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.current_depth += 1; + let result = t.super_visit_with(self); + self.current_depth -= 1; + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + // if we are only looking for "constrained" region, we have to + // ignore the inputs to a projection, as they may not appear + // in the normalized form + if self.just_constrained { + match t.sty { + ty::TyProjection(..) => { return false; } + _ => { } + } + } + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region) -> bool { + match r { + ty::ReLateBound(debruijn, br) if debruijn.depth == self.current_depth => { + self.regions.insert(br); + } + _ => { } + } + false + } +} + diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index e0abe1aebd28..9fca6d3d2013 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -197,7 +197,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { FutureIncompatibleInfo { id: LintId::of(OBJECT_UNSAFE_FRAGMENT), reference: "issue #33243 ", - } + }, + FutureIncompatibleInfo { + id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE), + reference: "issue #33685 ", + }, ]); // We have one lint pass defined specially diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 4faefb610562..ac86b7c87406 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -52,13 +52,17 @@ use middle::const_val::ConstVal; use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr}; use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::ErrKind::ErroneousReferencedConstant; +use hir::{self, SelfKind}; use hir::def::{self, Def}; use hir::def_id::DefId; +use hir::print as pprust; use middle::resolve_lifetime as rl; +use rustc::lint; use rustc::ty::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace}; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable}; use rustc::ty::wf::object_region_bounds; +use rustc_back::slice; use require_c_abi_if_variadic; use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope, @@ -74,10 +78,6 @@ use syntax::errors::DiagnosticBuilder; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::parse::token::{self, keywords}; -use rustc::hir::print as pprust; -use rustc::hir::{self, SelfKind}; -use rustc_back::slice; - pub trait AstConv<'gcx, 'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>; @@ -679,6 +679,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { PathParamMode::Explicit, trait_def_id, self_ty, + trait_ref.ref_id, trait_ref.path.segments.last().unwrap(), poly_projections) } @@ -723,6 +724,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { span: Span, param_mode: PathParamMode, trait_def_id: DefId, + trait_path_ref_id: ast::NodeId, trait_segment: &hir::PathSegment, mut projections: &mut Vec>) -> ty::PolyTraitRef<'tcx> @@ -732,6 +734,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode, trait_def_id, None, + trait_path_ref_id, trait_segment, projections) } @@ -742,6 +745,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode: PathParamMode, trait_def_id: DefId, self_ty: Option>, + path_id: ast::NodeId, trait_segment: &hir::PathSegment, poly_projections: &mut Vec>) -> ty::PolyTraitRef<'tcx> @@ -770,7 +774,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { .filter_map(|binding| { // specify type to assert that error was already reported in Err case: let predicate: Result<_, ErrorReported> = - self.ast_type_binding_to_poly_projection_predicate(poly_trait_ref.clone(), + self.ast_type_binding_to_poly_projection_predicate(path_id, + poly_trait_ref.clone(), self_ty, binding); predicate.ok() // ok to ignore Err() because ErrorReported (see above) @@ -863,7 +868,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { (self.tcx().mk_substs(substs), assoc_bindings) } - fn ast_type_binding_to_poly_projection_predicate(&self, + fn ast_type_binding_to_poly_projection_predicate( + &self, + path_id: ast::NodeId, mut trait_ref: ty::PolyTraitRef<'tcx>, self_ty: Option>, binding: &ConvertedBinding<'tcx>) @@ -887,6 +894,36 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { // // We want to produce `>::T == foo`. + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref. These are not wellformed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref); + let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&ty::Binder(binding.ty)); + debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); + 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, + _ => { + span_bug!( + binding.span, + "anonymous bound region {:?} in binding but not trait ref", + 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)); + } + // Simple case: X is defined in the current trait. if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { return Ok(ty::Binder(ty::ProjectionPredicate { // <-------------------+ @@ -1012,6 +1049,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { path.span, PathParamMode::Explicit, trait_def_id, + ty.id, path.segments.last().unwrap(), &mut projection_bounds); Ok((trait_ref, projection_bounds)) @@ -1416,6 +1454,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode: PathParamMode, def: Def, opt_self_ty: Option>, + base_path_ref_id: ast::NodeId, base_segments: &[hir::PathSegment]) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1434,6 +1473,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { span, param_mode, trait_def_id, + base_path_ref_id, base_segments.last().unwrap(), &mut projection_bounds); @@ -1518,6 +1558,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode: PathParamMode, mut def: Def, opt_self_ty: Option>, + base_path_ref_id: ast::NodeId, base_segments: &[hir::PathSegment], assoc_segments: &[hir::PathSegment]) -> (Ty<'tcx>, Def) { @@ -1532,6 +1573,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode, def, opt_self_ty, + base_path_ref_id, base_segments); debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", ty); // If any associated type segments remain, attempt to resolve them. @@ -1607,7 +1649,45 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } hir::TyBareFn(ref bf) => { require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); - tcx.mk_fn_ptr(self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl)) + let bare_fn_ty = self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl); + + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not wellformed. + // + // Example: + // + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + // + // Note that we do this check **here** and not in + // `ty_of_bare_fn` because the latter is also used to make + // the types for fn items, and we do not want to issue a + // warning then. (Once we fix #32330, the regions we are + // checking for here would be considered early bound + // anyway.) + let inputs = bare_fn_ty.sig.inputs(); + let late_bound_in_args = tcx.collect_constrained_late_bound_regions(&inputs); + let output = bare_fn_ty.sig.output(); + 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, + _ => { + span_bug!( + bf.decl.output.span(), + "anonymous bound region {:?} in return but not args", + 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)); + } + tcx.mk_fn_ptr(bare_fn_ty) } hir::TyPolyTraitRef(ref bounds) => { self.conv_ty_poly_trait_ref(rscope, ast_ty.span, bounds) @@ -1635,6 +1715,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { PathParamMode::Explicit, def, opt_self_ty, + ast_ty.id, &path.segments[..base_ty_end], &path.segments[base_ty_end..]); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ed2edc30c9d3..1ee9d1032a6e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3866,6 +3866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { PathParamMode::Optional, def, opt_self_ty, + node_id, &ty_segments[..base_ty_end], &ty_segments[base_ty_end..]); let item_segment = path.segments.last().unwrap(); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index f9a22e2a577b..5896a34b0d16 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -568,7 +568,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let (fty, explicit_self_category) = AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), - sig, untransformed_rcvr_ty); + sig, + untransformed_rcvr_ty); let def_id = ccx.tcx.map.local_def_id(id); let substs = mk_item_substs(ccx, &ty_generics); diff --git a/src/test/compile-fail/associated-types-eq-hr.rs b/src/test/compile-fail/associated-types-eq-hr.rs index d5678c155fd2..52a2ca9082d2 100644 --- a/src/test/compile-fail/associated-types-eq-hr.rs +++ b/src/test/compile-fail/associated-types-eq-hr.rs @@ -40,6 +40,17 @@ impl<'a> TheTrait<&'a isize> for UintStruct { } } +struct Tuple { +} + +impl<'a> TheTrait<(&'a isize, &'a isize)> for Tuple { + type A = &'a isize; + + fn get(&self, t: (&'a isize, &'a isize)) -> &'a isize { + t.0 + } +} + fn foo() where T : for<'x> TheTrait<&'x isize, A = &'x isize> { @@ -52,10 +63,28 @@ fn bar() // ok for UintStruct, but not IntStruct } -fn baz() - where T : for<'x,'y> TheTrait<&'x isize, A = &'y isize> +fn tuple_one() + where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> { - // not ok for either struct, due to the use of two lifetimes + // not ok for tuple, two lifetimes and we pick first +} + +fn tuple_two() + where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> +{ + // not ok for tuple, two lifetimes and we pick second +} + +fn tuple_three() + where T : for<'x> TheTrait<(&'x isize, &'x isize), A = &'x isize> +{ + // ok for tuple +} + +fn tuple_four() + where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)> +{ + // not ok for tuple, two lifetimes, and lifetime matching is invariant } pub fn main() { @@ -65,6 +94,16 @@ pub fn main() { bar::(); //~ ERROR type mismatch bar::(); - baz::(); //~ ERROR type mismatch - baz::(); //~ ERROR type mismatch + tuple_one::(); + //~^ ERROR E0277 + //~| ERROR type mismatch + + tuple_two::(); + //~^ ERROR E0277 + //~| ERROR type mismatch + + tuple_three::(); + + tuple_four::(); + //~^ ERROR E0277 } diff --git a/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs new file mode 100644 index 000000000000..f60f06b4ec83 --- /dev/null +++ b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs @@ -0,0 +1,66 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: func object clause + +#![allow(dead_code)] +#![feature(rustc_attrs)] +#![feature(unboxed_closures)] +#![deny(hr_lifetime_in_assoc_type)] + +trait Foo<'a> { + type Item; +} + +impl<'a> Foo<'a> for() { + 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 +} + +// 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 +} + +#[cfg(object)] +fn object1(_: Box Fn(<() as Foo<'a>>::Item) -> &'a i32>) { + //[object]~^ ERROR `Output` references lifetime `'a` + //[object]~| WARNING previously accepted +} + +#[cfg(object)] +fn object2(_: Box Fn() -> <() as Foo<'a>>::Item>) { + //[object]~^ ERROR `Output` references lifetime `'a` + //[object]~| WARNING previously accepted +} + +#[cfg(clause)] +fn clause1() 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() where T: for<'a> Fn() -> <() as Foo<'a>>::Item { + //[clause]~^ ERROR `Output` references lifetime `'a` + //[clause]~| WARNING previously accepted +} + +#[rustc_error] +fn main() { } //[ok]~ ERROR compilation successful diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs new file mode 100644 index 000000000000..020c9e5e1db5 --- /dev/null +++ b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs @@ -0,0 +1,90 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: angle paren ok elision + +#![allow(dead_code)] +#![feature(rustc_attrs)] +#![feature(unboxed_closures)] +#![deny(hr_lifetime_in_assoc_type)] + +trait Foo { + type Item; +} + +#[cfg(angle)] +fn angle Foo>() { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(angle)] +fn angle1() where T: for<'a> Foo { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(angle)] +fn angle2() where for<'a> T: Foo { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(angle)] +fn angle3(_: &for<'a> Foo) { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(paren)] +fn paren Fn() -> &'a i32>() { + //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a` + //[paren]~| WARNING previously accepted +} + +#[cfg(paren)] +fn paren1() 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() 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)] +fn elision &i32>() { + //[elision]~^ ERROR E0106 +} + +struct Parameterized<'a> { x: &'a str } + +#[cfg(ok)] +fn ok1 Fn(&Parameterized<'a>) -> &'a i32>() { +} + +#[cfg(ok)] +fn ok2 Fn<(&'b Parameterized<'a>,), Output=&'a i32>>() { +} + +#[cfg(ok)] +fn ok3() where for<'a> Parameterized<'a>: Foo { +} + +#[rustc_error] +fn main() { } //[ok]~ ERROR compilation successful diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs new file mode 100644 index 000000000000..0b4a9bf58a66 --- /dev/null +++ b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: sig local structure ok elision + +#![allow(dead_code)] +#![feature(rustc_attrs)] +#![feature(unboxed_closures)] +#![deny(hr_lifetime_in_assoc_type)] + +trait Foo { + type Item; +} + +#[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)] +fn elision(_: fn() -> &i32) { + //[elision]~^ ERROR E0106 +} + +struct Parameterized<'a> { x: &'a str } + +#[cfg(ok)] +fn ok1(_: &for<'a> Fn(&Parameterized<'a>) -> &'a i32) { +} + +#[cfg(ok)] +fn ok2(_: &for<'a,'b> Fn<(&'b Parameterized<'a>,), Output=&'a i32>) { +} + +#[rustc_error] +fn main() { } //[ok]~ ERROR compilation successful