Skip to content

Warnings for issue #32330 #33137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
@@ -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
)
}
}
469 changes: 281 additions & 188 deletions src/librustc/traits/project.rs

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions src/librustc/ty/fold.rs
Original file line number Diff line number Diff line change
@@ -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<T>(&self, value: &Binder<T>)
-> FnvHashSet<ty::BoundRegion>
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<T>(&self, value: &Binder<T>)
-> FnvHashSet<ty::BoundRegion>
where T : TypeFoldable<'tcx>
{
self.collect_late_bound_regions(value, false)
}

fn collect_late_bound_regions<T>(&self, value: &Binder<T>, just_constraint: bool)
-> FnvHashSet<ty::BoundRegion>
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<T>(self, value: &Binder<T>) -> T
@@ -625,3 +654,54 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
false
}
}

/// Collects all the late-bound regions it finds into a hash set.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment might want to live on the public function instead.

struct LateBoundRegionsCollector {
current_depth: u32,
regions: FnvHashSet<ty::BoundRegion>,
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<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> 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
}
}

6 changes: 5 additions & 1 deletion src/librustc_lint/lib.rs
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/33243>",
}
},
FutureIncompatibleInfo {
id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE),
reference: "issue #33685 <https://github.com/rust-lang/rust/issues/33685>",
},
]);

// We have one lint pass defined specially
95 changes: 88 additions & 7 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
@@ -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::PolyProjectionPredicate<'tcx>>)
-> 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<Ty<'tcx>>,
path_id: ast::NodeId,
trait_segment: &hir::PathSegment,
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
-> 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<Ty<'tcx>>,
binding: &ConvertedBinding<'tcx>)
@@ -887,6 +894,36 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
//
// We want to produce `<B as SuperTrait<int>>::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> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::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<Ty<'tcx>>,
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<Ty<'tcx>>,
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..]);

1 change: 1 addition & 0 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
@@ -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();
3 changes: 2 additions & 1 deletion src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
@@ -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);
60 changes: 30 additions & 30 deletions src/libsyntax/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -180,7 +180,7 @@ impl error::Error for ExplicitBug {
#[must_use]
#[derive(Clone)]
pub struct DiagnosticBuilder<'a> {
emitter: &'a RefCell<Box<Emitter>>,
handler: &'a Handler,
level: Level,
message: String,
code: Option<String>,
@@ -204,8 +204,9 @@ impl<'a> DiagnosticBuilder<'a> {
return;
}

self.emitter.borrow_mut().emit_struct(&self);
self.handler.emit.borrow_mut().emit_struct(&self);
self.cancel();
self.handler.panic_if_treat_err_as_bug();

// if self.is_fatal() {
// panic!(FatalError);
@@ -321,11 +322,11 @@ impl<'a> DiagnosticBuilder<'a> {

/// Convenience function for internal use, clients should use one of the
/// struct_* methods on Handler.
fn new(emitter: &'a RefCell<Box<Emitter>>,
fn new(handler: &'a Handler,
level: Level,
message: &str) -> DiagnosticBuilder<'a> {
DiagnosticBuilder {
emitter: emitter,
handler: handler,
level: level,
message: message.to_owned(),
code: None,
@@ -362,10 +363,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
impl<'a> Drop for DiagnosticBuilder<'a> {
fn drop(&mut self) {
if !panicking() && !self.cancelled() {
self.emitter.borrow_mut().emit(&MultiSpan::new(),
"Error constructed but not emitted",
None,
Bug);
self.handler.emit.borrow_mut().emit(&MultiSpan::new(),
"Error constructed but not emitted",
None,
Bug);
panic!();
}
}
@@ -412,14 +413,14 @@ impl Handler {
}

pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
DiagnosticBuilder::new(&self.emit, Level::Cancelled, "")
DiagnosticBuilder::new(self, Level::Cancelled, "")
}

pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
result.set_span(sp);
if !self.can_emit_warnings {
result.cancel();
@@ -431,7 +432,7 @@ impl Handler {
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
result.set_span(sp);
result.code(code.to_owned());
if !self.can_emit_warnings {
@@ -440,7 +441,7 @@ impl Handler {
result
}
pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
if !self.can_emit_warnings {
result.cancel();
}
@@ -451,7 +452,7 @@ impl Handler {
msg: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
result.set_span(sp);
result
}
@@ -461,21 +462,21 @@ impl Handler {
code: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
result.set_span(sp);
result.code(code.to_owned());
result
}
pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
self.bump_err_count();
DiagnosticBuilder::new(&self.emit, Level::Error, msg)
DiagnosticBuilder::new(self, Level::Error, msg)
}
pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
result.set_span(sp);
result
}
@@ -485,14 +486,14 @@ impl Handler {
code: &str)
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
result.set_span(sp);
result.code(code.to_owned());
result
}
pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
self.bump_err_count();
DiagnosticBuilder::new(&self.emit, Level::Fatal, msg)
DiagnosticBuilder::new(self, Level::Fatal, msg)
}

pub fn cancel(&mut self, err: &mut DiagnosticBuilder) {
@@ -503,36 +504,35 @@ impl Handler {
err.cancel();
}

pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
fn panic_if_treat_err_as_bug(&self) {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
panic!("encountered error with `-Z treat_err_as_bug");
}
}

pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str)
-> FatalError {
self.emit(&sp.into(), msg, Fatal);
self.bump_err_count();
self.panic_if_treat_err_as_bug();
return FatalError;
}
pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str)
-> FatalError {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
-> FatalError {
self.emit_with_code(&sp.into(), msg, code, Fatal);
self.bump_err_count();
self.panic_if_treat_err_as_bug();
return FatalError;
}
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit(&sp.into(), msg, Error);
self.bump_err_count();
self.panic_if_treat_err_as_bug();
}
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit_with_code(&sp.into(), msg, code, Error);
self.bump_err_count();
self.panic_if_treat_err_as_bug();
}
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(&sp.into(), msg, Warning);
49 changes: 44 additions & 5 deletions src/test/compile-fail/associated-types-eq-hr.rs
Original file line number Diff line number Diff line change
@@ -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<T>()
where T : for<'x> TheTrait<&'x isize, A = &'x isize>
{
@@ -52,10 +63,28 @@ fn bar<T>()
// ok for UintStruct, but not IntStruct
}

fn baz<T>()
where T : for<'x,'y> TheTrait<&'x isize, A = &'y isize>
fn tuple_one<T>()
where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize>
{
// not ok for tuple, two lifetimes and we pick first
}

fn tuple_two<T>()
where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>
{
// not ok for either struct, due to the use of two lifetimes
// not ok for tuple, two lifetimes and we pick second
}

fn tuple_three<T>()
where T : for<'x> TheTrait<(&'x isize, &'x isize), A = &'x isize>
{
// ok for tuple
}

fn tuple_four<T>()
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::<IntStruct>(); //~ ERROR type mismatch
bar::<UintStruct>();

baz::<IntStruct>(); //~ ERROR type mismatch
baz::<UintStruct>(); //~ ERROR type mismatch
tuple_one::<Tuple>();
//~^ ERROR E0277
//~| ERROR type mismatch

tuple_two::<Tuple>();
//~^ ERROR E0277
//~| ERROR type mismatch

tuple_three::<Tuple>();

tuple_four::<Tuple>();
//~^ ERROR E0277
}
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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<for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32>) {
//[object]~^ ERROR `Output` references lifetime `'a`
//[object]~| WARNING previously accepted
}

#[cfg(object)]
fn object2(_: Box<for<'a> Fn() -> <() as Foo<'a>>::Item>) {
//[object]~^ ERROR `Output` references lifetime `'a`
//[object]~| WARNING previously accepted
}

#[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]
fn main() { } //[ok]~ ERROR compilation successful
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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<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)]
fn elision<T: Fn() -> &i32>() {
//[elision]~^ ERROR E0106
}

struct Parameterized<'a> { x: &'a str }

#[cfg(ok)]
fn ok1<T: for<'a> Fn(&Parameterized<'a>) -> &'a i32>() {
}

#[cfg(ok)]
fn ok2<T: for<'a,'b> Fn<(&'b Parameterized<'a>,), Output=&'a i32>>() {
}

#[cfg(ok)]
fn ok3<T>() where for<'a> Parameterized<'a>: Foo<Item=&'a i32> {
}

#[rustc_error]
fn main() { } //[ok]~ ERROR compilation successful
Original file line number Diff line number Diff line change
@@ -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.

// 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