Skip to content

Commit 75e23e1

Browse files
committed
Auto merge of #33137 - nikomatsakis:issue-32330-lbr-in-return-type-warning-2, r=aturon
Warnings for issue #32330 This is an extension of the previous PR that issues warnings in more situations than before. It does not handle *all* cases of #32330 but I believe it issues warnings for all cases I've seen in practice. Before merging I'd like to address: - open a good issue explaining the problem and how to fix it (I have a [draft writeup][]) - work on the error message, which I think is not as clear as it could/should be (suggestions welcome) r? @aturon [draft writeup]: https://gist.github.com/nikomatsakis/631ec8b4af9a18b5d062d9d9b7d3d967
2 parents 0667ae9 + 639890d commit 75e23e1

File tree

12 files changed

+760
-233
lines changed

12 files changed

+760
-233
lines changed

src/librustc/lint/builtin.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ declare_lint! {
167167
"transmute from function item type to pointer-sized type erroneously allowed"
168168
}
169169

170+
declare_lint! {
171+
pub HR_LIFETIME_IN_ASSOC_TYPE,
172+
Warn,
173+
"binding for associated type references higher-ranked lifetime \
174+
that does not appear in the trait input types"
175+
}
176+
170177
declare_lint! {
171178
pub OVERLAPPING_INHERENT_IMPLS,
172179
Warn,
@@ -234,7 +241,8 @@ impl LintPass for HardwiredLints {
234241
RENAMED_AND_REMOVED_LINTS,
235242
SUPER_OR_SELF_IN_GLOBAL_PATH,
236243
UNSIZED_IN_TUPLE,
237-
OBJECT_UNSAFE_FRAGMENT
244+
OBJECT_UNSAFE_FRAGMENT,
245+
HR_LIFETIME_IN_ASSOC_TYPE
238246
)
239247
}
240248
}

src/librustc/traits/project.rs

+281-188
Large diffs are not rendered by default.

src/librustc/ty/fold.rs

+80
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,35 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
382382
}
383383
}
384384

385+
/// Returns a set of all late-bound regions that are constrained
386+
/// by `value`, meaning that if we instantiate those LBR with
387+
/// variables and equate `value` with something else, those
388+
/// variables will also be equated.
389+
pub fn collect_constrained_late_bound_regions<T>(&self, value: &Binder<T>)
390+
-> FnvHashSet<ty::BoundRegion>
391+
where T : TypeFoldable<'tcx>
392+
{
393+
self.collect_late_bound_regions(value, true)
394+
}
395+
396+
/// Returns a set of all late-bound regions that appear in `value` anywhere.
397+
pub fn collect_referenced_late_bound_regions<T>(&self, value: &Binder<T>)
398+
-> FnvHashSet<ty::BoundRegion>
399+
where T : TypeFoldable<'tcx>
400+
{
401+
self.collect_late_bound_regions(value, false)
402+
}
403+
404+
fn collect_late_bound_regions<T>(&self, value: &Binder<T>, just_constraint: bool)
405+
-> FnvHashSet<ty::BoundRegion>
406+
where T : TypeFoldable<'tcx>
407+
{
408+
let mut collector = LateBoundRegionsCollector::new(just_constraint);
409+
let result = value.skip_binder().visit_with(&mut collector);
410+
assert!(!result); // should never have stopped early
411+
collector.regions
412+
}
413+
385414
/// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also
386415
/// method lookup and a few other places where precise region relationships are not required.
387416
pub fn erase_late_bound_regions<T>(self, value: &Binder<T>) -> T
@@ -625,3 +654,54 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
625654
false
626655
}
627656
}
657+
658+
/// Collects all the late-bound regions it finds into a hash set.
659+
struct LateBoundRegionsCollector {
660+
current_depth: u32,
661+
regions: FnvHashSet<ty::BoundRegion>,
662+
just_constrained: bool,
663+
}
664+
665+
impl LateBoundRegionsCollector {
666+
fn new(just_constrained: bool) -> Self {
667+
LateBoundRegionsCollector {
668+
current_depth: 1,
669+
regions: FnvHashSet(),
670+
just_constrained: just_constrained,
671+
}
672+
}
673+
}
674+
675+
impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
676+
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool {
677+
self.current_depth += 1;
678+
let result = t.super_visit_with(self);
679+
self.current_depth -= 1;
680+
result
681+
}
682+
683+
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
684+
// if we are only looking for "constrained" region, we have to
685+
// ignore the inputs to a projection, as they may not appear
686+
// in the normalized form
687+
if self.just_constrained {
688+
match t.sty {
689+
ty::TyProjection(..) => { return false; }
690+
_ => { }
691+
}
692+
}
693+
694+
t.super_visit_with(self)
695+
}
696+
697+
fn visit_region(&mut self, r: ty::Region) -> bool {
698+
match r {
699+
ty::ReLateBound(debruijn, br) if debruijn.depth == self.current_depth => {
700+
self.regions.insert(br);
701+
}
702+
_ => { }
703+
}
704+
false
705+
}
706+
}
707+

src/librustc_lint/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
197197
FutureIncompatibleInfo {
198198
id: LintId::of(OBJECT_UNSAFE_FRAGMENT),
199199
reference: "issue #33243 <https://github.com/rust-lang/rust/issues/33243>",
200-
}
200+
},
201+
FutureIncompatibleInfo {
202+
id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE),
203+
reference: "issue #33685 <https://github.com/rust-lang/rust/issues/33685>",
204+
},
201205
]);
202206

203207
// We have one lint pass defined specially

src/librustc_typeck/astconv.rs

+88-7
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,17 @@ use middle::const_val::ConstVal;
5252
use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr};
5353
use rustc_const_eval::EvalHint::UncheckedExprHint;
5454
use rustc_const_eval::ErrKind::ErroneousReferencedConstant;
55+
use hir::{self, SelfKind};
5556
use hir::def::{self, Def};
5657
use hir::def_id::DefId;
58+
use hir::print as pprust;
5759
use middle::resolve_lifetime as rl;
60+
use rustc::lint;
5861
use rustc::ty::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};
5962
use rustc::traits;
6063
use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
6164
use rustc::ty::wf::object_region_bounds;
65+
use rustc_back::slice;
6266
use require_c_abi_if_variadic;
6367
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
6468
ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope,
@@ -74,10 +78,6 @@ use syntax::errors::DiagnosticBuilder;
7478
use syntax::feature_gate::{GateIssue, emit_feature_err};
7579
use syntax::parse::token::{self, keywords};
7680

77-
use rustc::hir::print as pprust;
78-
use rustc::hir::{self, SelfKind};
79-
use rustc_back::slice;
80-
8181
pub trait AstConv<'gcx, 'tcx> {
8282
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
8383

@@ -679,6 +679,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
679679
PathParamMode::Explicit,
680680
trait_def_id,
681681
self_ty,
682+
trait_ref.ref_id,
682683
trait_ref.path.segments.last().unwrap(),
683684
poly_projections)
684685
}
@@ -723,6 +724,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
723724
span: Span,
724725
param_mode: PathParamMode,
725726
trait_def_id: DefId,
727+
trait_path_ref_id: ast::NodeId,
726728
trait_segment: &hir::PathSegment,
727729
mut projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
728730
-> ty::PolyTraitRef<'tcx>
@@ -732,6 +734,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
732734
param_mode,
733735
trait_def_id,
734736
None,
737+
trait_path_ref_id,
735738
trait_segment,
736739
projections)
737740
}
@@ -742,6 +745,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
742745
param_mode: PathParamMode,
743746
trait_def_id: DefId,
744747
self_ty: Option<Ty<'tcx>>,
748+
path_id: ast::NodeId,
745749
trait_segment: &hir::PathSegment,
746750
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
747751
-> ty::PolyTraitRef<'tcx>
@@ -770,7 +774,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
770774
.filter_map(|binding| {
771775
// specify type to assert that error was already reported in Err case:
772776
let predicate: Result<_, ErrorReported> =
773-
self.ast_type_binding_to_poly_projection_predicate(poly_trait_ref.clone(),
777+
self.ast_type_binding_to_poly_projection_predicate(path_id,
778+
poly_trait_ref.clone(),
774779
self_ty,
775780
binding);
776781
predicate.ok() // ok to ignore Err() because ErrorReported (see above)
@@ -863,7 +868,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
863868
(self.tcx().mk_substs(substs), assoc_bindings)
864869
}
865870

866-
fn ast_type_binding_to_poly_projection_predicate(&self,
871+
fn ast_type_binding_to_poly_projection_predicate(
872+
&self,
873+
path_id: ast::NodeId,
867874
mut trait_ref: ty::PolyTraitRef<'tcx>,
868875
self_ty: Option<Ty<'tcx>>,
869876
binding: &ConvertedBinding<'tcx>)
@@ -887,6 +894,36 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
887894
//
888895
// We want to produce `<B as SuperTrait<int>>::T == foo`.
889896

897+
// Find any late-bound regions declared in `ty` that are not
898+
// declared in the trait-ref. These are not wellformed.
899+
//
900+
// Example:
901+
//
902+
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
903+
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
904+
let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref);
905+
let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&ty::Binder(binding.ty));
906+
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
907+
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
908+
for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
909+
let br_name = match *br {
910+
ty::BrNamed(_, name) => name,
911+
_ => {
912+
span_bug!(
913+
binding.span,
914+
"anonymous bound region {:?} in binding but not trait ref",
915+
br);
916+
}
917+
};
918+
tcx.sess.add_lint(
919+
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
920+
path_id,
921+
binding.span,
922+
format!("binding for associated type `{}` references lifetime `{}`, \
923+
which does not appear in the trait input types",
924+
binding.item_name, br_name));
925+
}
926+
890927
// Simple case: X is defined in the current trait.
891928
if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
892929
return Ok(ty::Binder(ty::ProjectionPredicate { // <-------------------+
@@ -1012,6 +1049,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
10121049
path.span,
10131050
PathParamMode::Explicit,
10141051
trait_def_id,
1052+
ty.id,
10151053
path.segments.last().unwrap(),
10161054
&mut projection_bounds);
10171055
Ok((trait_ref, projection_bounds))
@@ -1416,6 +1454,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
14161454
param_mode: PathParamMode,
14171455
def: Def,
14181456
opt_self_ty: Option<Ty<'tcx>>,
1457+
base_path_ref_id: ast::NodeId,
14191458
base_segments: &[hir::PathSegment])
14201459
-> Ty<'tcx> {
14211460
let tcx = self.tcx();
@@ -1434,6 +1473,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
14341473
span,
14351474
param_mode,
14361475
trait_def_id,
1476+
base_path_ref_id,
14371477
base_segments.last().unwrap(),
14381478
&mut projection_bounds);
14391479

@@ -1518,6 +1558,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
15181558
param_mode: PathParamMode,
15191559
mut def: Def,
15201560
opt_self_ty: Option<Ty<'tcx>>,
1561+
base_path_ref_id: ast::NodeId,
15211562
base_segments: &[hir::PathSegment],
15221563
assoc_segments: &[hir::PathSegment])
15231564
-> (Ty<'tcx>, Def) {
@@ -1532,6 +1573,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
15321573
param_mode,
15331574
def,
15341575
opt_self_ty,
1576+
base_path_ref_id,
15351577
base_segments);
15361578
debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", ty);
15371579
// If any associated type segments remain, attempt to resolve them.
@@ -1607,7 +1649,45 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
16071649
}
16081650
hir::TyBareFn(ref bf) => {
16091651
require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span);
1610-
tcx.mk_fn_ptr(self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl))
1652+
let bare_fn_ty = self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl);
1653+
1654+
// Find any late-bound regions declared in return type that do
1655+
// not appear in the arguments. These are not wellformed.
1656+
//
1657+
// Example:
1658+
//
1659+
// for<'a> fn() -> &'a str <-- 'a is bad
1660+
// for<'a> fn(&'a String) -> &'a str <-- 'a is ok
1661+
//
1662+
// Note that we do this check **here** and not in
1663+
// `ty_of_bare_fn` because the latter is also used to make
1664+
// the types for fn items, and we do not want to issue a
1665+
// warning then. (Once we fix #32330, the regions we are
1666+
// checking for here would be considered early bound
1667+
// anyway.)
1668+
let inputs = bare_fn_ty.sig.inputs();
1669+
let late_bound_in_args = tcx.collect_constrained_late_bound_regions(&inputs);
1670+
let output = bare_fn_ty.sig.output();
1671+
let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
1672+
for br in late_bound_in_ret.difference(&late_bound_in_args) {
1673+
let br_name = match *br {
1674+
ty::BrNamed(_, name) => name,
1675+
_ => {
1676+
span_bug!(
1677+
bf.decl.output.span(),
1678+
"anonymous bound region {:?} in return but not args",
1679+
br);
1680+
}
1681+
};
1682+
tcx.sess.add_lint(
1683+
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
1684+
ast_ty.id,
1685+
ast_ty.span,
1686+
format!("return type references lifetime `{}`, \
1687+
which does not appear in the trait input types",
1688+
br_name));
1689+
}
1690+
tcx.mk_fn_ptr(bare_fn_ty)
16111691
}
16121692
hir::TyPolyTraitRef(ref bounds) => {
16131693
self.conv_ty_poly_trait_ref(rscope, ast_ty.span, bounds)
@@ -1635,6 +1715,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
16351715
PathParamMode::Explicit,
16361716
def,
16371717
opt_self_ty,
1718+
ast_ty.id,
16381719
&path.segments[..base_ty_end],
16391720
&path.segments[base_ty_end..]);
16401721

src/librustc_typeck/check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3866,6 +3866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
38663866
PathParamMode::Optional,
38673867
def,
38683868
opt_self_ty,
3869+
node_id,
38693870
&ty_segments[..base_ty_end],
38703871
&ty_segments[base_ty_end..]);
38713872
let item_segment = path.segments.last().unwrap();

src/librustc_typeck/collect.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
568568

569569
let (fty, explicit_self_category) =
570570
AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
571-
sig, untransformed_rcvr_ty);
571+
sig,
572+
untransformed_rcvr_ty);
572573

573574
let def_id = ccx.tcx.map.local_def_id(id);
574575
let substs = mk_item_substs(ccx, &ty_generics);

0 commit comments

Comments
 (0)