Skip to content

Commit ef10e94

Browse files
committed
Correctly handle named lifetimes.
Enhances annotation logic to properly consider named lifetimes where lifetime elision rules that were previously implemented would not apply. Further, adds new help and note messages to diagnostics and highlights only lifetime when dealing with named lifetimes.
1 parent 0eabba8 commit ef10e94

9 files changed

+319
-111
lines changed

src/librustc/ty/sty.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,23 @@ impl_stable_hash_for!(struct DebruijnIndex { private });
13241324

13251325
/// Region utilities
13261326
impl RegionKind {
1327+
/// Is this region named by the user?
1328+
pub fn has_name(&self) -> bool {
1329+
match *self {
1330+
RegionKind::ReEarlyBound(ebr) => ebr.has_name(),
1331+
RegionKind::ReLateBound(_, br) => br.is_named(),
1332+
RegionKind::ReFree(fr) => fr.bound_region.is_named(),
1333+
RegionKind::ReScope(..) => false,
1334+
RegionKind::ReStatic => true,
1335+
RegionKind::ReVar(..) => false,
1336+
RegionKind::ReSkolemized(_, br) => br.is_named(),
1337+
RegionKind::ReEmpty => false,
1338+
RegionKind::ReErased => false,
1339+
RegionKind::ReClosureBound(..) => false,
1340+
RegionKind::ReCanonical(..) => false,
1341+
}
1342+
}
1343+
13271344
pub fn is_late_bound(&self) -> bool {
13281345
match *self {
13291346
ty::ReLateBound(..) => true,

src/librustc_mir/borrow_check/error_reporting.rs

+189-91
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc::mir::{
1717
LocalDecl, LocalKind, Location, Operand, Place, ProjectionElem, Rvalue, Statement,
1818
StatementKind, VarBindingForm,
1919
};
20+
use rustc::hir;
2021
use rustc::hir::def_id::DefId;
2122
use rustc::ty;
2223
use rustc_data_structures::fx::FxHashSet;
@@ -524,10 +525,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
524525
err.span_label(
525526
drop_span,
526527
format!(
527-
"...but `{}` is only valid for the duration of the `{}` function, so it \
528-
is dropped here while still borrowed",
529-
name,
530-
self.infcx.tcx.hir.name(fn_node_id),
528+
"but `{}` will be dropped here, when the function `{}` returns",
529+
name, self.infcx.tcx.hir.name(fn_node_id),
531530
)
532531
);
533532

@@ -1235,67 +1234,137 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12351234
let fn_node_id = self.infcx.tcx.hir.as_local_node_id(did)?;
12361235
let fn_decl = self.infcx.tcx.hir.fn_decl(fn_node_id)?;
12371236

1238-
// If there is one argument and this error is being reported, that means
1239-
// that the lifetime of the borrow could not be made to match the single
1240-
// argument's lifetime. We can highlight it.
1237+
// We need to work out which arguments to highlight. We do this by looking
1238+
// at the return type, where there are three cases:
12411239
//
1242-
// If there is more than one argument and this error is being reported, that
1243-
// means there must be a self parameter - as otherwise there would be an error
1244-
// from lifetime elision and not this. So we highlight the self parameter.
1245-
let argument_span = fn_decl.inputs.first()?.span;
1246-
let argument_ty = sig.inputs().skip_binder().first()?;
1247-
if is_closure {
1248-
// Closure arguments are wrapped in a tuple, so we need to get the first
1249-
// from that.
1250-
let argument_ty = if let ty::TyKind::Tuple(elems) = argument_ty.sty {
1251-
let argument_ty = elems.first()?;
1252-
if let ty::TyKind::Ref(_, _, _) = argument_ty.sty {
1253-
argument_ty
1254-
} else {
1240+
// 1. If there are named arguments, then we should highlight the return type and
1241+
// highlight any of the arguments that are also references with that lifetime.
1242+
// If there are no arguments that have the same lifetime as the return type,
1243+
// then don't highlight anything.
1244+
// 2. The return type is a reference with an anonymous lifetime. If this is
1245+
// the case, then we can take advantage of (and teach) the lifetime elision
1246+
// rules.
1247+
//
1248+
// We know that an error is being reported. So the arguments and return type
1249+
// must satisfy the elision rules. Therefore, if there is a single argument
1250+
// then that means the return type and first (and only) argument have the same
1251+
// lifetime and the borrow isn't meeting that, we can highlight the argument
1252+
// and return type.
1253+
//
1254+
// If there are multiple arguments then the first argument must be self (else
1255+
// it would not satisfy the elision rules), so we can highlight self and the
1256+
// return type.
1257+
// 3. The return type is not a reference. In this case, we don't highlight
1258+
// anything.
1259+
let return_ty = sig.output();
1260+
match return_ty.skip_binder().sty {
1261+
ty::TyKind::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
1262+
// This is case 1 from above, return type is a named reference so we need to
1263+
// search for relevant arguments.
1264+
let mut arguments = Vec::new();
1265+
for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
1266+
if let ty::TyKind::Ref(argument_region, _, _) = argument.sty {
1267+
if argument_region == return_region {
1268+
// Need to use the `rustc::ty` types to compare against the
1269+
// `return_region`. Then use the `rustc::hir` type to get only
1270+
// the lifetime span.
1271+
match &fn_decl.inputs[index].node {
1272+
hir::TyKind::Rptr(lifetime, _) => {
1273+
// With access to the lifetime, we can get
1274+
// the span of it.
1275+
arguments.push((*argument, lifetime.span));
1276+
},
1277+
_ => bug!("ty type is a ref but hir type is not"),
1278+
}
1279+
}
1280+
}
1281+
}
1282+
1283+
// We need to have arguments. This shouldn't happen, but it's worth checking.
1284+
if arguments.is_empty() {
12551285
return None;
12561286
}
1257-
} else {
1258-
return None
1259-
};
12601287

1261-
Some(AnnotatedBorrowFnSignature::Closure {
1262-
argument_ty,
1263-
argument_span,
1264-
})
1265-
} else if let ty::TyKind::Ref(argument_region, _, _) = argument_ty.sty {
1266-
// We only consider the return type for functions.
1267-
let return_span = fn_decl.output.span();
1268-
1269-
let return_ty = sig.output();
1270-
let (return_region, return_ty) = if let ty::TyKind::Ref(
1271-
return_region, _, _
1272-
) = return_ty.skip_binder().sty {
1273-
(return_region, *return_ty.skip_binder())
1274-
} else {
1275-
return None;
1276-
};
1288+
// We use a mix of the HIR and the Ty types to get information
1289+
// as the HIR doesn't have full types for closure arguments.
1290+
let return_ty = *sig.output().skip_binder();
1291+
let mut return_span = fn_decl.output.span();
1292+
if let hir::FunctionRetTy::Return(ty) = fn_decl.output {
1293+
if let hir::TyKind::Rptr(lifetime, _) = ty.into_inner().node {
1294+
return_span = lifetime.span;
1295+
}
1296+
}
12771297

1278-
Some(AnnotatedBorrowFnSignature::Function {
1279-
argument_ty,
1280-
argument_span,
1281-
return_ty,
1282-
return_span,
1283-
regions_equal: return_region == argument_region,
1284-
})
1285-
} else {
1286-
None
1298+
Some(AnnotatedBorrowFnSignature::NamedFunction {
1299+
arguments,
1300+
return_ty,
1301+
return_span,
1302+
})
1303+
},
1304+
ty::TyKind::Ref(_, _, _) if is_closure => {
1305+
// This is case 2 from above but only for closures, return type is anonymous
1306+
// reference so we select
1307+
// the first argument.
1308+
let argument_span = fn_decl.inputs.first()?.span;
1309+
let argument_ty = sig.inputs().skip_binder().first()?;
1310+
1311+
// Closure arguments are wrapped in a tuple, so we need to get the first
1312+
// from that.
1313+
if let ty::TyKind::Tuple(elems) = argument_ty.sty {
1314+
let argument_ty = elems.first()?;
1315+
if let ty::TyKind::Ref(_, _, _) = argument_ty.sty {
1316+
return Some(AnnotatedBorrowFnSignature::Closure {
1317+
argument_ty,
1318+
argument_span,
1319+
});
1320+
}
1321+
}
1322+
1323+
None
1324+
},
1325+
ty::TyKind::Ref(_, _, _) => {
1326+
// This is also case 2 from above but for functions, return type is still an
1327+
// anonymous reference so we select the first argument.
1328+
let argument_span = fn_decl.inputs.first()?.span;
1329+
let argument_ty = sig.inputs().skip_binder().first()?;
1330+
1331+
let return_span = fn_decl.output.span();
1332+
let return_ty = *sig.output().skip_binder();
1333+
1334+
// We expect the first argument to be a reference.
1335+
match argument_ty.sty {
1336+
ty::TyKind::Ref(_, _, _) => {},
1337+
_ => return None,
1338+
}
1339+
1340+
Some(AnnotatedBorrowFnSignature::AnonymousFunction {
1341+
argument_ty,
1342+
argument_span,
1343+
return_ty,
1344+
return_span,
1345+
})
1346+
},
1347+
_ => {
1348+
// This is case 3 from above, return type is not a reference so don't highlight
1349+
// anything.
1350+
None
1351+
},
12871352
}
12881353
}
12891354
}
12901355

12911356
#[derive(Debug)]
12921357
enum AnnotatedBorrowFnSignature<'tcx> {
1293-
Function {
1358+
NamedFunction {
1359+
arguments: Vec<(ty::Ty<'tcx>, Span)>,
1360+
return_ty: ty::Ty<'tcx>,
1361+
return_span: Span,
1362+
},
1363+
AnonymousFunction {
12941364
argument_ty: ty::Ty<'tcx>,
12951365
argument_span: Span,
12961366
return_ty: ty::Ty<'tcx>,
12971367
return_span: Span,
1298-
regions_equal: bool,
12991368
},
13001369
Closure {
13011370
argument_ty: ty::Ty<'tcx>,
@@ -1304,57 +1373,86 @@ enum AnnotatedBorrowFnSignature<'tcx> {
13041373
}
13051374

13061375
impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
1376+
/// Annotate the provided diagnostic with information about borrow from the fn signature that
1377+
/// helps explain.
13071378
fn emit(
13081379
&self,
13091380
diag: &mut DiagnosticBuilder<'_>
13101381
) -> String {
1311-
let (argument_ty, argument_span) = match self {
1312-
AnnotatedBorrowFnSignature::Function {
1313-
argument_ty,
1314-
argument_span,
1315-
..
1316-
} => (argument_ty, argument_span),
1317-
AnnotatedBorrowFnSignature::Closure {
1382+
match self {
1383+
AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
1384+
diag.span_label(
1385+
*argument_span,
1386+
format!("has type `{}`", self.get_name_for_ty(argument_ty, 0)),
1387+
);
1388+
1389+
self.get_region_name_for_ty(argument_ty, 0)
1390+
},
1391+
AnnotatedBorrowFnSignature::AnonymousFunction {
13181392
argument_ty,
13191393
argument_span,
1320-
} => (argument_ty, argument_span),
1321-
};
1394+
return_ty,
1395+
return_span,
1396+
} => {
1397+
let argument_ty_name = self.get_name_for_ty(argument_ty, 0);
1398+
diag.span_label(
1399+
*argument_span,
1400+
format!("has type `{}`", argument_ty_name)
1401+
);
13221402

1323-
let (argument_region_name, argument_ty_name) = (
1324-
self.get_region_name_for_ty(argument_ty, 0),
1325-
self.get_name_for_ty(argument_ty, 0),
1326-
);
1327-
diag.span_label(
1328-
*argument_span,
1329-
format!("has type `{}`", argument_ty_name)
1330-
);
1403+
let return_ty_name = self.get_name_for_ty(return_ty, 0);
1404+
let types_equal = return_ty_name == argument_ty_name;
1405+
diag.span_label(
1406+
*return_span,
1407+
format!(
1408+
"{}has type `{}`",
1409+
if types_equal { "also " } else { "" },
1410+
return_ty_name,
1411+
)
1412+
);
13311413

1332-
// Only emit labels for the return value when we're annotating a function.
1333-
if let AnnotatedBorrowFnSignature::Function {
1334-
return_ty,
1335-
return_span,
1336-
regions_equal,
1337-
..
1338-
} = self {
1339-
let counter = if *regions_equal { 0 } else { 1 };
1340-
let (return_region_name, return_ty_name) = (
1341-
self.get_region_name_for_ty(return_ty, counter),
1342-
self.get_name_for_ty(return_ty, counter)
1343-
);
1414+
diag.note(
1415+
"argument and return type have the same lifetime due to lifetime elision rules",
1416+
);
1417+
diag.note(
1418+
"to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-\
1419+
lifetime-syntax.html#lifetime-elision>",
1420+
);
13441421

1345-
let types_equal = return_ty_name == argument_ty_name;
1346-
diag.span_label(
1347-
*return_span,
1348-
format!(
1349-
"{}has type `{}`",
1350-
if types_equal { "also " } else { "" },
1351-
return_ty_name,
1352-
)
1353-
);
1422+
self.get_region_name_for_ty(return_ty, 0)
1423+
},
1424+
AnnotatedBorrowFnSignature::NamedFunction {
1425+
arguments,
1426+
return_ty,
1427+
return_span,
1428+
} => {
1429+
// Region of return type and arguments checked to be the same earlier.
1430+
let region_name = self.get_region_name_for_ty(return_ty, 0);
1431+
for (_, argument_span) in arguments {
1432+
diag.span_label(
1433+
*argument_span,
1434+
format!("has lifetime `{}`", region_name)
1435+
);
1436+
}
13541437

1355-
return_region_name
1356-
} else {
1357-
argument_region_name
1438+
diag.span_label(
1439+
*return_span,
1440+
format!(
1441+
"also has lifetime `{}`",
1442+
region_name,
1443+
)
1444+
);
1445+
1446+
diag.help(
1447+
&format!(
1448+
"use data from the highlighted arguments which match the `{}` lifetime of \
1449+
the return type",
1450+
region_name,
1451+
),
1452+
);
1453+
1454+
region_name
1455+
},
13581456
}
13591457
}
13601458

src/test/ui/issues/issue-30438-c.nll.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ error[E0597]: `x` does not live long enough
22
--> $DIR/issue-30438-c.rs:19:5
33
|
44
LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
5-
| ------------ ---------------------------- has type `&'y <Test<'z> as Trait>::Out`
6-
| |
7-
| has type `&'y Test<'z>`
5+
| -- -- also has lifetime `'y`
6+
| |
7+
| has lifetime `'y`
88
LL | let x = Test { s: "this cannot last" };
99
LL | &x
1010
| ^^ `x` would have to be valid for `'y`
1111
LL | //~^ ERROR: `x` does not live long enough
1212
LL | }
13-
| - ...but `x` is only valid for the duration of the `silly` function, so it is dropped here while still borrowed
13+
| - but `x` will be dropped here, when the function `silly` returns
1414
|
15+
= help: use data from the highlighted arguments which match the `'y` lifetime of the return type
1516
= note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
1617
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
1718

0 commit comments

Comments
 (0)