Skip to content

Commit f1661d2

Browse files
authored
Rollup merge of #72543 - estebank:opaque-missing-lts-in-fn, r=nikomatsakis
Account for missing lifetime in opaque and trait object return types When encountering an opaque closure return type that needs to bound a lifetime to the function's arguments, including borrows and type params, provide appropriate suggestions that lead to working code. Get the user from ```rust fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() where G: Get<T> { move || { *dest = g.get(); } } ``` to ```rust fn foo<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() +'a where G: Get<T> { move || { *dest = g.get(); } } ```
2 parents 74e8046 + 83f6f22 commit f1661d2

File tree

51 files changed

+834
-654
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+834
-654
lines changed

src/librustc_infer/infer/error_reporting/mod.rs

+124-46
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use rustc_errors::{pluralize, struct_span_err};
6060
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
6161
use rustc_hir as hir;
6262
use rustc_hir::def_id::DefId;
63-
use rustc_hir::Node;
63+
use rustc_hir::{Item, ItemKind, Node};
6464
use rustc_middle::ty::error::TypeError;
6565
use rustc_middle::ty::{
6666
self,
@@ -1682,49 +1682,92 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16821682
bound_kind: GenericKind<'tcx>,
16831683
sub: Region<'tcx>,
16841684
) -> DiagnosticBuilder<'a> {
1685+
let hir = &self.tcx.hir();
16851686
// Attempt to obtain the span of the parameter so we can
16861687
// suggest adding an explicit lifetime bound to it.
1687-
let type_param_span = match (self.in_progress_tables, bound_kind) {
1688-
(Some(ref table), GenericKind::Param(ref param)) => {
1689-
let table_owner = table.borrow().hir_owner;
1690-
table_owner.and_then(|table_owner| {
1691-
let generics = self.tcx.generics_of(table_owner.to_def_id());
1692-
// Account for the case where `param` corresponds to `Self`,
1693-
// which doesn't have the expected type argument.
1694-
if !(generics.has_self && param.index == 0) {
1695-
let type_param = generics.type_param(param, self.tcx);
1696-
let hir = &self.tcx.hir();
1697-
type_param.def_id.as_local().map(|def_id| {
1698-
// Get the `hir::Param` to verify whether it already has any bounds.
1699-
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
1700-
// instead we suggest `T: 'a + 'b` in that case.
1701-
let id = hir.as_local_hir_id(def_id);
1702-
let mut has_bounds = false;
1703-
if let Node::GenericParam(param) = hir.get(id) {
1704-
has_bounds = !param.bounds.is_empty();
1705-
}
1706-
let sp = hir.span(id);
1707-
// `sp` only covers `T`, change it so that it covers
1708-
// `T:` when appropriate
1709-
let is_impl_trait = bound_kind.to_string().starts_with("impl ");
1710-
let sp = if has_bounds && !is_impl_trait {
1711-
sp.to(self
1712-
.tcx
1713-
.sess
1714-
.source_map()
1715-
.next_point(self.tcx.sess.source_map().next_point(sp)))
1716-
} else {
1717-
sp
1718-
};
1719-
(sp, has_bounds, is_impl_trait)
1720-
})
1688+
let generics =
1689+
self.in_progress_tables.and_then(|table| table.borrow().hir_owner).map(|table_owner| {
1690+
let hir_id = hir.as_local_hir_id(table_owner);
1691+
let parent_id = hir.get_parent_item(hir_id);
1692+
(
1693+
// Parent item could be a `mod`, so we check the HIR before calling:
1694+
if let Some(Node::Item(Item {
1695+
kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
1696+
..
1697+
})) = hir.find(parent_id)
1698+
{
1699+
Some(self.tcx.generics_of(hir.local_def_id(parent_id).to_def_id()))
17211700
} else {
17221701
None
1723-
}
1724-
})
1702+
},
1703+
self.tcx.generics_of(table_owner.to_def_id()),
1704+
)
1705+
});
1706+
let type_param_span = match (generics, bound_kind) {
1707+
(Some((_, ref generics)), GenericKind::Param(ref param)) => {
1708+
// Account for the case where `param` corresponds to `Self`,
1709+
// which doesn't have the expected type argument.
1710+
if !(generics.has_self && param.index == 0) {
1711+
let type_param = generics.type_param(param, self.tcx);
1712+
type_param.def_id.as_local().map(|def_id| {
1713+
// Get the `hir::Param` to verify whether it already has any bounds.
1714+
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
1715+
// instead we suggest `T: 'a + 'b` in that case.
1716+
let id = hir.as_local_hir_id(def_id);
1717+
let mut has_bounds = false;
1718+
if let Node::GenericParam(param) = hir.get(id) {
1719+
has_bounds = !param.bounds.is_empty();
1720+
}
1721+
let sp = hir.span(id);
1722+
// `sp` only covers `T`, change it so that it covers
1723+
// `T:` when appropriate
1724+
let is_impl_trait = bound_kind.to_string().starts_with("impl ");
1725+
let sp = if has_bounds && !is_impl_trait {
1726+
sp.to(self
1727+
.tcx
1728+
.sess
1729+
.source_map()
1730+
.next_point(self.tcx.sess.source_map().next_point(sp)))
1731+
} else {
1732+
sp
1733+
};
1734+
(sp, has_bounds, is_impl_trait)
1735+
})
1736+
} else {
1737+
None
1738+
}
17251739
}
17261740
_ => None,
17271741
};
1742+
let new_lt = generics
1743+
.as_ref()
1744+
.and_then(|(parent_g, g)| {
1745+
let possible: Vec<_> = (b'a'..=b'z').map(|c| format!("'{}", c as char)).collect();
1746+
let mut lts_names = g
1747+
.params
1748+
.iter()
1749+
.filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
1750+
.map(|p| p.name.as_str())
1751+
.collect::<Vec<_>>();
1752+
if let Some(g) = parent_g {
1753+
lts_names.extend(
1754+
g.params
1755+
.iter()
1756+
.filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
1757+
.map(|p| p.name.as_str()),
1758+
);
1759+
}
1760+
let lts = lts_names.iter().map(|s| -> &str { &*s }).collect::<Vec<_>>();
1761+
possible.into_iter().find(|candidate| !lts.contains(&candidate.as_str()))
1762+
})
1763+
.unwrap_or("'lt".to_string());
1764+
let add_lt_sugg = generics
1765+
.as_ref()
1766+
.and_then(|(_, g)| g.params.first())
1767+
.and_then(|param| param.def_id.as_local())
1768+
.map(|def_id| {
1769+
(hir.span(hir.as_local_hir_id(def_id)).shrink_to_lo(), format!("{}, ", new_lt))
1770+
});
17281771

17291772
let labeled_user_string = match bound_kind {
17301773
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
@@ -1781,6 +1824,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
17811824
}
17821825
}
17831826

1827+
let new_binding_suggestion =
1828+
|err: &mut DiagnosticBuilder<'tcx>,
1829+
type_param_span: Option<(Span, bool, bool)>,
1830+
bound_kind: GenericKind<'tcx>| {
1831+
let msg = "consider introducing an explicit lifetime bound";
1832+
if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
1833+
let suggestion = if is_impl_trait {
1834+
(sp.shrink_to_hi(), format!(" + {}", new_lt))
1835+
} else {
1836+
let tail = if has_lifetimes { " +" } else { "" };
1837+
(sp, format!("{}: {}{}", bound_kind, new_lt, tail))
1838+
};
1839+
let mut sugg =
1840+
vec![suggestion, (span.shrink_to_hi(), format!(" + {}", new_lt))];
1841+
if let Some(lt) = add_lt_sugg {
1842+
sugg.push(lt);
1843+
sugg.rotate_right(1);
1844+
}
1845+
// `MaybeIncorrect` due to issue #41966.
1846+
err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
1847+
}
1848+
};
1849+
17841850
let mut err = match *sub {
17851851
ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. })
17861852
| ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }) => {
@@ -1822,17 +1888,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
18221888
"{} may not live long enough",
18231889
labeled_user_string
18241890
);
1825-
err.help(&format!(
1826-
"consider adding an explicit lifetime bound for `{}`",
1827-
bound_kind
1828-
));
18291891
note_and_explain_region(
18301892
self.tcx,
18311893
&mut err,
18321894
&format!("{} must be valid for ", labeled_user_string),
18331895
sub,
18341896
"...",
18351897
);
1898+
if let Some(infer::RelateParamBound(_, t)) = origin {
1899+
let t = self.resolve_vars_if_possible(&t);
1900+
match t.kind {
1901+
// We've got:
1902+
// fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
1903+
// suggest:
1904+
// fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
1905+
ty::Closure(_, _substs) | ty::Opaque(_, _substs) => {
1906+
new_binding_suggestion(&mut err, type_param_span, bound_kind);
1907+
}
1908+
_ => {
1909+
binding_suggestion(&mut err, type_param_span, bound_kind, new_lt);
1910+
}
1911+
}
1912+
}
18361913
err
18371914
}
18381915
};
@@ -1861,14 +1938,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
18611938
"...",
18621939
);
18631940

1941+
debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
1942+
debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
1943+
debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
1944+
debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
1945+
debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
1946+
18641947
if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) =
18651948
(&sup_origin, &sub_origin)
18661949
{
1867-
debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
1868-
debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
1869-
debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
1870-
debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
1871-
debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
18721950
debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace);
18731951
debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace);
18741952
debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values);

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
121121
(Some(ret_span), _) => (
122122
ty_sub.span,
123123
ret_span,
124-
"this parameter and the return type are declared \
125-
with different lifetimes..."
124+
"this parameter and the return type are declared with different lifetimes..."
126125
.to_owned(),
127126
format!("...but data{} is returned here", span_label_var1),
128127
),
129128
(_, Some(ret_span)) => (
130129
ty_sup.span,
131130
ret_span,
132-
"this parameter and the return type are declared \
133-
with different lifetimes..."
131+
"this parameter and the return type are declared with different lifetimes..."
134132
.to_owned(),
135133
format!("...but data{} is returned here", span_label_var1),
136134
),

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
5555
diag.emit();
5656
ErrorReported
5757
})
58+
.or_else(|| self.try_report_impl_not_conforming_to_trait())
5859
.or_else(|| self.try_report_anon_anon_conflict())
5960
.or_else(|| self.try_report_static_impl_trait())
60-
.or_else(|| self.try_report_impl_not_conforming_to_trait())
6161
}
6262

6363
pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {

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

+15-22
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
2121
// where the anonymous region appears (there must always be one; we
2222
// only introduced anonymous regions in parameters) as well as a
2323
// version new_ty of its type where the anonymous region is replaced
24-
// with the named one.//scope_def_id
25-
let (named, anon, anon_param_info, region_info) = if self.is_named_region(sub)
24+
// with the named one.
25+
let (named, anon, anon_param_info, region_info) = if sub.has_name()
2626
&& self.tcx().is_suitable_region(sup).is_some()
2727
&& self.find_param_with_region(sup, sub).is_some()
2828
{
@@ -32,7 +32,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
3232
self.find_param_with_region(sup, sub).unwrap(),
3333
self.tcx().is_suitable_region(sup).unwrap(),
3434
)
35-
} else if self.is_named_region(sup)
35+
} else if sup.has_name()
3636
&& self.tcx().is_suitable_region(sub).is_some()
3737
&& self.find_param_with_region(sub, sup).is_some()
3838
{
@@ -74,15 +74,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
7474
}
7575

7676
if let Some((_, fndecl)) = self.find_anon_type(anon, &br) {
77-
if self.is_return_type_anon(scope_def_id, br, fndecl).is_some()
78-
|| self.is_self_anon(is_first, scope_def_id)
79-
{
77+
let is_self_anon = self.is_self_anon(is_first, scope_def_id);
78+
if is_self_anon {
8079
return None;
8180
}
81+
8282
if let FnRetTy::Return(ty) = &fndecl.output {
83-
if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.kind, sub) {
84-
// This is an impl Trait return that evaluates de need of 'static.
85-
// We handle this case better in `static_impl_trait`.
83+
let mut v = ty::TraitObjectVisitor(vec![]);
84+
rustc_hir::intravisit::walk_ty(&mut v, ty);
85+
86+
debug!("try_report_named_anon_conflict: ret ty {:?}", ty);
87+
if sub == &ty::ReStatic && (matches!(ty.kind, TyKind::Def(_, _)) || v.0.len() == 1)
88+
{
89+
debug!("try_report_named_anon_conflict: impl Trait + 'static");
90+
// This is an `impl Trait` or `dyn Trait` return that evaluates de need of
91+
// `'static`. We handle this case better in `static_impl_trait`.
8692
return None;
8793
}
8894
}
@@ -114,17 +120,4 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
114120

115121
Some(diag)
116122
}
117-
118-
// This method returns whether the given Region is Named
119-
pub(super) fn is_named_region(&self, region: ty::Region<'tcx>) -> bool {
120-
match *region {
121-
ty::ReStatic => true,
122-
ty::ReFree(ref free_region) => match free_region.bound_region {
123-
ty::BrNamed(..) => true,
124-
_ => false,
125-
},
126-
ty::ReEarlyBound(ebr) => ebr.has_name(),
127-
_ => false,
128-
}
129-
}
130123
}

0 commit comments

Comments
 (0)