Skip to content

Commit bdb39ab

Browse files
committed
Increase accuracy of lifetime bound on trait object impl suggestion
1 parent e6599ad commit bdb39ab

18 files changed

+321
-114
lines changed

src/librustc_hir/hir.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -2197,7 +2197,17 @@ pub enum IsAsync {
21972197
NotAsync,
21982198
}
21992199

2200-
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
2200+
#[derive(
2201+
Copy,
2202+
Clone,
2203+
PartialEq,
2204+
RustcEncodable,
2205+
RustcDecodable,
2206+
Debug,
2207+
HashStable_Generic,
2208+
Eq,
2209+
Hash
2210+
)]
22012211
pub enum Defaultness {
22022212
Default { has_value: bool },
22032213
Final,

src/librustc_infer/infer/combine.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ use crate::traits::{Obligation, PredicateObligations};
3636

3737
use rustc_ast::ast;
3838
use rustc_hir::def_id::DefId;
39+
use rustc_middle::traits::ObligationCause;
3940
use rustc_middle::ty::error::TypeError;
4041
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
4142
use rustc_middle::ty::subst::SubstsRef;
4243
use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
4344
use rustc_middle::ty::{IntType, UintType};
44-
use rustc_span::{Span, DUMMY_SP};
45+
use rustc_span::DUMMY_SP;
4546

4647
#[derive(Clone)]
4748
pub struct CombineFields<'infcx, 'tcx> {
@@ -367,10 +368,11 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
367368
};
368369

369370
debug!("generalize: for_universe = {:?}", for_universe);
371+
debug!("generalize: trace = {:?}", self.trace);
370372

371373
let mut generalize = Generalizer {
372374
infcx: self.infcx,
373-
span: self.trace.cause.span,
375+
cause: &self.trace.cause,
374376
for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid),
375377
for_universe,
376378
ambient_variance,
@@ -414,7 +416,7 @@ struct Generalizer<'cx, 'tcx> {
414416
infcx: &'cx InferCtxt<'cx, 'tcx>,
415417

416418
/// The span, used when creating new type variables and things.
417-
span: Span,
419+
cause: &'cx ObligationCause<'tcx>,
418420

419421
/// The vid of the type variable that is in the process of being
420422
/// instantiated; if we find this within the type we are folding,
@@ -639,7 +641,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
639641

640642
// FIXME: This is non-ideal because we don't give a
641643
// very descriptive origin for this region variable.
642-
Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe))
644+
Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.cause.span), self.for_universe))
643645
}
644646

645647
fn consts(

src/librustc_infer/infer/error_reporting/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2007,7 +2007,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
20072007
infer::MiscVariable(_) => String::new(),
20082008
infer::PatternRegion(_) => " for pattern".to_string(),
20092009
infer::AddrOfRegion(_) => " for borrow expression".to_string(),
2010-
infer::Autoref(_) => " for autoref".to_string(),
2010+
infer::Autoref(_, _) => " for autoref".to_string(),
20112011
infer::Coercion(_) => " for automatic coercion".to_string(),
20122012
infer::LateBoundRegion(_, br, infer::FnCall) => {
20132013
format!(" for lifetime parameter {}in function call", br_string(br))

src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs

+136-48
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
33
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
44
use crate::infer::lexical_region_resolve::RegionResolutionError;
5+
use crate::infer::{SubregionOrigin, TypeTrace};
6+
use crate::traits::ObligationCauseCode;
57
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
6-
use rustc_hir::def::{DefKind, Res};
78
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
8-
use rustc_hir::{
9-
GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, Path, PolyTraitRef, TraitRef,
10-
TyKind,
11-
};
12-
use rustc_middle::ty::{self, RegionKind, Ty, TypeFoldable, TypeVisitor};
9+
use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor};
10+
use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
11+
use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
12+
use rustc_span::Span;
1313

1414
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
1515
/// Print the error message for lifetime errors when the return type is a static impl Trait.
@@ -27,6 +27,39 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
2727
) if **sub_r == RegionKind::ReStatic => {
2828
(var_origin, sub_origin, sub_r, sup_origin, sup_r)
2929
}
30+
RegionResolutionError::ConcreteFailure(
31+
SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
32+
sub_r,
33+
sup_r,
34+
) if **sub_r == RegionKind::ReStatic => {
35+
// This is for the implicit `'static` requirement coming from `impl dyn Trait {}`.
36+
if let ObligationCauseCode::UnifyReceiver(assoc) = &cause.code {
37+
let param = self.find_param_with_region(sup_r, sub_r)?;
38+
let lifetime = if sup_r.has_name() {
39+
format!("lifetime `{}`", sup_r)
40+
} else {
41+
"an anonymous lifetime `'_`".to_string()
42+
};
43+
let mut err = struct_span_err!(
44+
tcx.sess,
45+
cause.span,
46+
E0759,
47+
"cannot infer an appropriate lifetime"
48+
);
49+
err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
50+
err.span_label(
51+
cause.span,
52+
"...is captured and required to live as long as `'static` here",
53+
);
54+
if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &assoc.container) {
55+
err.emit();
56+
return Some(ErrorReported);
57+
} else {
58+
err.cancel();
59+
}
60+
}
61+
return None;
62+
}
3063
_ => return None,
3164
};
3265
debug!(
@@ -96,7 +129,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
96129
);
97130
}
98131

99-
self.find_impl_on_dyn_trait(&mut err, param.param_ty);
132+
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin {
133+
if let ObligationCauseCode::UnifyReceiver(assoc) = &cause.code {
134+
self.find_impl_on_dyn_trait(&mut err, param.param_ty, &assoc.container);
135+
}
136+
}
100137

101138
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
102139
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
@@ -222,63 +259,86 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
222259

223260
/// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
224261
/// `'static` obligation. Find `impl` blocks that are implemented
225-
fn find_impl_on_dyn_trait(&self, err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>) -> bool {
262+
fn find_impl_on_dyn_trait(
263+
&self,
264+
err: &mut DiagnosticBuilder<'_>,
265+
ty: Ty<'_>,
266+
container: &AssocItemContainer,
267+
) -> bool {
226268
let tcx = self.tcx();
269+
let mut suggested = false;
227270

228271
// Find the trait object types in the argument.
229272
let mut v = TraitObjectVisitor(vec![]);
230273
v.visit_ty(ty);
231-
debug!("TraitObjectVisitor {:?}", v.0);
232274

233-
// Find all the `impl`s in the local scope that can be called on the type parameter.
234-
// FIXME: this doesn't find `impl dyn Trait { /**/ }`.
275+
let container_id = match container {
276+
// When the obligation comes from an `impl Foo for dyn Bar {}`, we
277+
// have the `DefId` of the `trait` itself, not the relevant `impl`
278+
// block. Because of this, we have to look at all the `trait`s
279+
// available, and filter out all that are not of `Foo` (this `def_id`)
280+
// and not of `Bar` (the `filter_map` later in this method).
281+
AssocItemContainer::TraitContainer(def_id) => def_id,
282+
283+
// When the obligation comes from an `impl dyn Trait {}`, we already
284+
// have the `DefId` of the relevant `Item`, so we use it directly.
285+
AssocItemContainer::ImplContainer(def_id) => {
286+
if let Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) =
287+
tcx.hir().get_if_local(*def_id)
288+
{
289+
for found_did in &v.0 {
290+
let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
291+
hir_v.visit_ty(self_ty);
292+
if let [span] = &hir_v.0[..] {
293+
err.span_suggestion_verbose(
294+
span.shrink_to_hi(),
295+
"this `impl` introduces an implicit `'static` requirement, \
296+
consider changing it",
297+
" + '_".to_string(),
298+
Applicability::MaybeIncorrect,
299+
);
300+
suggested = true;
301+
}
302+
}
303+
}
304+
return suggested;
305+
}
306+
};
307+
308+
// Find all the `impl`s in the local scope that can be called on the type parameter. And
309+
// retain all that are `impl`s of the trait that originated the `'static` obligation.
310+
// This doesn't find `impl dyn Trait { /**/ }`, but that case is handled above.
235311
let impl_self_tys = tcx
236312
.all_traits(LOCAL_CRATE)
237313
.iter()
238314
.flat_map(|trait_did| tcx.hir().trait_impls(*trait_did))
239315
.filter_map(|impl_node| {
240316
let impl_did = tcx.hir().local_def_id(*impl_node);
241-
if let Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) =
242-
tcx.hir().get_if_local(impl_did.to_def_id())
243-
{
244-
Some(self_ty)
245-
} else {
246-
None
317+
match tcx.hir().get_if_local(impl_did.to_def_id()) {
318+
Some(Node::Item(Item {
319+
kind: ItemKind::Impl { self_ty, of_trait: Some(of_trait), .. },
320+
..
321+
})) if of_trait.trait_def_id() == Some(*container_id) => Some(self_ty),
322+
_ => None,
247323
}
248324
});
249-
let mut suggested = false;
325+
326+
// Given all the `impl`s of the relevant `trait`, look for those that are implemented for
327+
// the trait object in the `fn` parameter type.
250328
for self_ty in impl_self_tys {
251-
if let TyKind::TraitObject(
252-
poly_trait_refs,
253-
Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
254-
) = self_ty.kind
255-
{
256-
for p in poly_trait_refs {
257-
if let PolyTraitRef {
258-
trait_ref:
259-
TraitRef { path: Path { res: Res::Def(DefKind::Trait, did), .. }, .. },
260-
..
261-
} = p
262-
{
263-
for found_did in &v.0 {
264-
if did == found_did {
265-
// We've found an `impl Foo for dyn Bar {}`.
266-
// FIXME: we should change this so it also works for
267-
// `impl Foo for Box<dyn Bar> {}`.
268-
err.span_suggestion_verbose(
269-
self_ty.span.shrink_to_hi(),
270-
"this `impl` introduces an implicit `'static` requirement, \
271-
consider changing it",
272-
" + '_".to_string(),
273-
Applicability::MaybeIncorrect,
274-
);
275-
suggested = true;
276-
}
277-
}
278-
}
329+
for found_did in &v.0 {
330+
let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
331+
hir_v.visit_ty(self_ty);
332+
if let [span] = &hir_v.0[..] {
333+
err.span_suggestion_verbose(
334+
span.shrink_to_hi(),
335+
"this `impl` introduces an implicit `'static` requirement, \
336+
consider changing it",
337+
" + '_".to_string(),
338+
Applicability::MaybeIncorrect,
339+
);
340+
suggested = true;
279341
}
280-
err.emit();
281-
return Some(ErrorReported);
282342
}
283343
}
284344
suggested
@@ -301,3 +361,31 @@ impl TypeVisitor<'_> for TraitObjectVisitor {
301361
}
302362
}
303363
}
364+
365+
/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
366+
struct HirTraitObjectVisitor(Vec<Span>, DefId);
367+
368+
impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
369+
type Map = ErasedMap<'tcx>;
370+
371+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
372+
NestedVisitorMap::None
373+
}
374+
375+
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
376+
match t.kind {
377+
TyKind::TraitObject(
378+
poly_trait_refs,
379+
Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
380+
) => {
381+
for ptr in poly_trait_refs {
382+
if Some(self.1) == ptr.trait_ref.trait_def_id() {
383+
self.0.push(ptr.span);
384+
}
385+
}
386+
}
387+
_ => {}
388+
}
389+
walk_ty(self, t);
390+
}
391+
}

src/librustc_infer/infer/error_reporting/nice_region_error/util.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,18 @@ use rustc_hir::def_id::LocalDefId;
77
use rustc_middle::ty::{self, DefIdTree, Region, Ty};
88
use rustc_span::Span;
99

10-
// The struct contains the information about the anonymous region
11-
// we are searching for.
10+
/// Information about the anonymous region we are searching for.
1211
#[derive(Debug)]
1312
pub(super) struct AnonymousParamInfo<'tcx> {
14-
// the parameter corresponding to the anonymous region
13+
/// The parameter corresponding to the anonymous region.
1514
pub param: &'tcx hir::Param<'tcx>,
16-
// the type corresponding to the anonymopus region parameter
15+
/// The type corresponding to the anonymous region parameter.
1716
pub param_ty: Ty<'tcx>,
18-
// the ty::BoundRegion corresponding to the anonymous region
17+
/// The ty::BoundRegion corresponding to the anonymous region.
1918
pub bound_region: ty::BoundRegion,
20-
// param_ty_span contains span of parameter type
19+
/// The `Span` of the parameter type.
2120
pub param_ty_span: Span,
22-
// corresponds to id the argument is the first parameter
23-
// in the declaration
21+
/// Signals that the argument is the first parameter in the declaration.
2422
pub is_first: bool,
2523
}
2624

src/librustc_infer/infer/mod.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ pub enum RegionVariableOrigin {
463463
AddrOfRegion(Span),
464464

465465
/// Regions created as part of an autoref of a method receiver
466-
Autoref(Span),
466+
Autoref(Span, ty::AssocItem),
467467

468468
/// Regions created as part of an automatic coercion
469469
Coercion(Span),
@@ -1798,15 +1798,15 @@ impl<'tcx> SubregionOrigin<'tcx> {
17981798
impl RegionVariableOrigin {
17991799
pub fn span(&self) -> Span {
18001800
match *self {
1801-
MiscVariable(a) => a,
1802-
PatternRegion(a) => a,
1803-
AddrOfRegion(a) => a,
1804-
Autoref(a) => a,
1805-
Coercion(a) => a,
1806-
EarlyBoundRegion(a, ..) => a,
1807-
LateBoundRegion(a, ..) => a,
1801+
MiscVariable(a)
1802+
| PatternRegion(a)
1803+
| AddrOfRegion(a)
1804+
| Autoref(a, _)
1805+
| Coercion(a)
1806+
| EarlyBoundRegion(a, ..)
1807+
| LateBoundRegion(a, ..)
1808+
| UpvarRegion(_, a) => a,
18081809
BoundRegionInCoherence(_) => rustc_span::DUMMY_SP,
1809-
UpvarRegion(_, a) => a,
18101810
NLL(..) => bug!("NLL variable used with `span`"),
18111811
}
18121812
}

src/librustc_infer/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//! This API is completely unstable and subject to change.
1414
1515
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
16+
#![feature(bindings_after_at)]
1617
#![feature(bool_to_option)]
1718
#![feature(box_patterns)]
1819
#![feature(box_syntax)]

src/librustc_middle/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ pub enum ObligationCauseCode<'tcx> {
299299
/// Method receiver
300300
MethodReceiver,
301301

302+
UnifyReceiver(Rc<ty::AssocItem>),
303+
302304
/// `return` with no expression
303305
ReturnNoExpression,
304306

src/librustc_middle/traits/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
211211
super::StartFunctionType => Some(super::StartFunctionType),
212212
super::IntrinsicType => Some(super::IntrinsicType),
213213
super::MethodReceiver => Some(super::MethodReceiver),
214+
super::UnifyReceiver(ref assoc) => Some(super::UnifyReceiver(assoc.clone())),
214215
super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)),
215216
super::TrivialBound => Some(super::TrivialBound),
216217
}

0 commit comments

Comments
 (0)