From 46f427bee9ac60d4ce31baa95430223d5ec110b3 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 11 Jul 2017 23:12:06 +0300 Subject: [PATCH] Fix incorrect subst index Fix treatment of lifetimes defined in nested types during detection of late bound regions in signatures. Do not replace substs with inference variables when "cannot specify lifetime arguments explicitly..." is reported as a lint. --- src/librustc_typeck/check/method/confirm.rs | 17 +++++++------ src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/collect.rs | 9 ++++--- .../method-call-lifetime-args-lint.rs | 20 +++++++++++++++ .../method-call-lifetime-args-subst-index.rs | 25 +++++++++++++++++++ .../compile-fail/method-call-lifetime-args.rs | 11 -------- 6 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 src/test/compile-fail/method-call-lifetime-args-subst-index.rs diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index c28ddf876b3c..ad4ee5a9d6dc 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -281,7 +281,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { fn instantiate_method_substs(&mut self, pick: &probe::Pick<'tcx>, segment: &hir::PathSegment, - substs: &Substs<'tcx>) + parent_substs: &Substs<'tcx>) -> &'tcx Substs<'tcx> { // Determine the values for the generic parameters of the method. // If they were not explicitly supplied, just construct fresh @@ -296,20 +296,23 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { hir::AngleBracketedParameters(ref data) => (&data.types, &data.lifetimes), _ => bug!("unexpected generic arguments: {:?}", segment.parameters), }; + assert_eq!(method_generics.parent_count(), parent_substs.len()); Substs::for_item(self.tcx, pick.item.def_id, |def, _| { let i = def.index as usize; - if i < substs.len() { - substs.region_at(i) - } else if let Some(lifetime) = supplied_lifetimes.get(i - substs.len()) { + if i < parent_substs.len() { + parent_substs.region_at(i) + } else if let Some(lifetime) = + supplied_lifetimes.get(i - parent_substs.len()) { AstConv::ast_region_to_region(self.fcx, lifetime, Some(def)) } else { self.region_var_for_def(self.span, def) } }, |def, cur_substs| { let i = def.index as usize; - if i < substs.len() { - substs.type_at(i) - } else if let Some(ast_ty) = supplied_types.get(i - substs.len()) { + if i < parent_substs.len() { + parent_substs.type_at(i) + } else if let Some(ast_ty) = + supplied_types.get(i - parent_substs.len() - method_generics.regions.len()) { self.to_ty(ast_ty) } else { self.type_var_for_def(self.span, def, cur_substs) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 917bffbc22f0..af11cacb247b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4697,13 +4697,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tcx.sess.span_err(lifetimes[0].span, "cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present"); + *segment = None; } else { self.tcx.sess.add_lint(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, lifetimes[0].id, lifetimes[0].span, format!("cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present")); } - *segment = None; return; } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 32ccfc511fc4..72bd084330dd 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -777,7 +777,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, -> bool { struct LateBoundRegionsDetector<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - binder_depth: usize, + binder_depth: u32, has_late_bound_regions: bool, } @@ -812,7 +812,10 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match self.tcx.named_region_map.defs.get(<.id).cloned() { Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {} - _ => self.has_late_bound_regions = true + Some(rl::Region::LateBound(debruijn, _)) | + Some(rl::Region::LateBoundAnon(debruijn, _)) + if debruijn.depth < self.binder_depth => {} + _ => self.has_late_bound_regions = true, } } } @@ -822,7 +825,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, decl: &'tcx hir::FnDecl) -> bool { let mut visitor = LateBoundRegionsDetector { - tcx, binder_depth: 0, has_late_bound_regions: false + tcx, binder_depth: 1, has_late_bound_regions: false }; for lifetime in &generics.lifetimes { if tcx.named_region_map.late_bound.contains(&lifetime.lifetime.id) { diff --git a/src/test/compile-fail/method-call-lifetime-args-lint.rs b/src/test/compile-fail/method-call-lifetime-args-lint.rs index 9bf34de92fe8..b206924e538f 100644 --- a/src/test/compile-fail/method-call-lifetime-args-lint.rs +++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs @@ -17,6 +17,14 @@ impl S { fn late_implicit(self, _: &u8, _: &u8) {} fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} } fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} } + + // 'late lifetimes here belong to nested types not to the tested functions. + fn early_tricky_explicit<'a>(_: for<'late> fn(&'late u8), + _: Box Fn(&'late u8)>) + -> &'a u8 { loop {} } + fn early_tricky_implicit<'a>(_: fn(&u8), + _: Box) + -> &'a u8 { loop {} } } fn method_call() { @@ -61,6 +69,9 @@ fn method_call() { S.late_implicit_early::<'static, 'static, 'static>(&0); //~^ ERROR cannot specify lifetime arguments explicitly //~| WARN this was previously accepted + + S::early_tricky_explicit::<'static>(loop {}, loop {}); // OK + S::early_tricky_implicit::<'static>(loop {}, loop {}); // OK } fn ufcs() { @@ -73,4 +84,13 @@ fn ufcs() { //~| WARN this was previously accepted } +fn lint_not_inference_error() { + fn f<'early, 'late, T: 'early>() {} + + // Make sure `u8` is substituted and not replaced with an inference variable + f::<'static, u8>; + //~^ ERROR cannot specify lifetime arguments explicitly + //~| WARN this was previously accepted +} + fn main() {} diff --git a/src/test/compile-fail/method-call-lifetime-args-subst-index.rs b/src/test/compile-fail/method-call-lifetime-args-subst-index.rs new file mode 100644 index 000000000000..a9505e4f936a --- /dev/null +++ b/src/test/compile-fail/method-call-lifetime-args-subst-index.rs @@ -0,0 +1,25 @@ +// Copyright 2017 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. + +#![feature(rustc_attrs)] +#![allow(unused)] + +struct S; + +impl S { + fn early_and_type<'a, T>(self) -> &'a T { loop {} } +} + +fn test() { + S.early_and_type::(); +} + +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/method-call-lifetime-args.rs b/src/test/compile-fail/method-call-lifetime-args.rs index c29701804a74..f0a87c747038 100644 --- a/src/test/compile-fail/method-call-lifetime-args.rs +++ b/src/test/compile-fail/method-call-lifetime-args.rs @@ -19,14 +19,6 @@ impl S { fn late_implicit_self_early<'b>(&self) -> &'b u8 { loop {} } fn late_unused_early<'a, 'b>(self) -> &'b u8 { loop {} } fn life_and_type<'a, T>(self) -> &'a T { loop {} } - - // 'late lifetimes here belong to nested types not to the tested functions. - fn early_tricky_explicit<'a>(_: for<'late> fn(&'late u8), - _: Box Fn(&'late u8)>) - -> &'a u8 { loop {} } - fn early_tricky_implicit<'a>(_: fn(&u8), - _: Box) - -> &'a u8 { loop {} } } fn method_call() { @@ -85,9 +77,6 @@ fn ufcs() { let _: &u8 = S::life_and_type::<'static>(S); S::life_and_type::(S); S::life_and_type::<'static, u8>(S); - - S::early_tricky_explicit::<'static>(loop {}, loop {}); // OK - S::early_tricky_implicit::<'static>(loop {}, loop {}); // OK } fn main() {}