Skip to content

Commit 9e351c8

Browse files
Add more information to cycle detection error messages
1 parent 52ae96f commit 9e351c8

File tree

5 files changed

+121
-28
lines changed

5 files changed

+121
-28
lines changed

source/rust_verify/src/rust_to_vir.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ fn check_item<'tcx>(
344344
)?);
345345
}
346346
let types = Arc::new(types);
347+
let path_span = path.span.to(impll.self_ty.span);
347348
let path = def_id_to_vir_path(ctxt.tcx, &ctxt.verus_items, path.res.def_id());
348349
let (typ_params, typ_bounds) = crate::rust_to_vir_base::check_generics_bounds_fun(
349350
ctxt.tcx,
@@ -358,7 +359,7 @@ fn check_item<'tcx>(
358359
typ_bounds,
359360
trait_path: path.clone(),
360361
trait_typ_args: types.clone(),
361-
trait_typ_arg_impls: impl_paths,
362+
trait_typ_arg_impls: ctxt.spanned_new(path_span, impl_paths),
362363
};
363364
vir.trait_impls.push(ctxt.spanned_new(item.span, trait_impl));
364365
Some((trait_ref, path, types))
@@ -498,7 +499,7 @@ fn check_item<'tcx>(
498499
typ,
499500
impl_paths: Arc::new(impl_paths),
500501
};
501-
vir.assoc_type_impls.push(ctxt.spanned_new(item.span, assocx));
502+
vir.assoc_type_impls.push(ctxt.spanned_new(impl_item.span, assocx));
502503
} else {
503504
unsupported_err!(
504505
item.span,

source/vir/src/ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ pub struct TraitImplX {
988988
pub typ_bounds: GenericBounds,
989989
pub trait_path: Path,
990990
pub trait_typ_args: Typs,
991-
pub trait_typ_arg_impls: ImplPaths,
991+
pub trait_typ_arg_impls: Arc<Spanned<ImplPaths>>,
992992
}
993993

994994
#[derive(Clone, Debug, Hash, Serialize, Deserialize, ToDebugSNode, PartialEq, Eq)]

source/vir/src/context.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub struct GlobalCtx {
4141
pub func_call_graph: Arc<Graph<Node>>,
4242
pub func_call_sccs: Arc<Vec<Node>>,
4343
pub(crate) datatype_graph: Arc<Graph<crate::recursive_types::TypNode>>,
44+
pub(crate) datatype_graph_span_infos: Vec<Span>,
4445
/// Connects quantifier identifiers to the original expression
4546
pub qid_map: RefCell<HashMap<String, BndInfo>>,
4647
pub(crate) rlimit: f32,
@@ -256,14 +257,24 @@ impl GlobalCtx {
256257
func_call_graph.add_node(Node::TraitImpl(t.x.impl_path.clone()));
257258
}
258259

260+
let mut span_infos: Vec<Span> = Vec::new();
259261
for t in &krate.trait_impls {
260-
crate::recursive_types::add_trait_impl_to_graph(&mut func_call_graph, t);
262+
crate::recursive_types::add_trait_impl_to_graph(
263+
&mut span_infos,
264+
&mut func_call_graph,
265+
t,
266+
);
261267
}
262268

263269
for f in &krate.functions {
264270
fun_bounds.insert(f.x.name.clone(), f.x.typ_bounds.clone());
265271
func_call_graph.add_node(Node::Fun(f.x.name.clone()));
266-
crate::recursion::expand_call_graph(&func_map, &mut func_call_graph, f)?;
272+
crate::recursion::expand_call_graph(
273+
&func_map,
274+
&mut func_call_graph,
275+
&mut span_infos,
276+
f,
277+
)?;
267278
}
268279

269280
func_call_graph.compute_sccs();
@@ -292,7 +303,7 @@ impl GlobalCtx {
292303
}
293304
let qid_map = RefCell::new(HashMap::new());
294305

295-
let datatype_graph = crate::recursive_types::build_datatype_graph(krate);
306+
let datatype_graph = crate::recursive_types::build_datatype_graph(krate, &mut span_infos);
296307

297308
Ok(GlobalCtx {
298309
chosen_triggers,
@@ -302,6 +313,7 @@ impl GlobalCtx {
302313
func_call_graph: Arc::new(func_call_graph),
303314
func_call_sccs: Arc::new(func_call_sccs),
304315
datatype_graph: Arc::new(datatype_graph),
316+
datatype_graph_span_infos: span_infos,
305317
qid_map,
306318
rlimit,
307319
interpreter_log,
@@ -322,6 +334,7 @@ impl GlobalCtx {
322334
no_span: self.no_span.clone(),
323335
func_call_graph: self.func_call_graph.clone(),
324336
datatype_graph: self.datatype_graph.clone(),
337+
datatype_graph_span_infos: self.datatype_graph_span_infos.clone(),
325338
func_call_sccs: self.func_call_sccs.clone(),
326339
qid_map,
327340
rlimit: self.rlimit,

source/vir/src/recursion.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ pub enum Node {
3333
Datatype(Path),
3434
Trait(Path),
3535
TraitImpl(Path),
36+
// This is used to replace an X --> Y edge with X --> SpanInfo --> Y edges
37+
// to give more precise span information than X or Y alone provide
38+
SpanInfo { span_infos_index: usize, text: String },
3639
}
3740

3841
#[derive(Clone)]
@@ -420,6 +423,7 @@ pub(crate) fn check_termination_stm(
420423
pub(crate) fn expand_call_graph(
421424
func_map: &HashMap<Fun, Function>,
422425
call_graph: &mut Graph<Node>,
426+
span_infos: &mut Vec<Span>,
423427
function: &Function,
424428
) -> Result<(), VirErr> {
425429
// See recursive_types::check_traits for more documentation
@@ -488,7 +492,15 @@ pub(crate) fn expand_call_graph(
488492
continue;
489493
}
490494
}
491-
call_graph.add_edge(f_node.clone(), Node::TraitImpl(impl_path.clone()));
495+
let expr_node = crate::recursive_types::new_span_info_node(
496+
span_infos,
497+
expr.span.clone(),
498+
": application of a function to some type arguments, which may depend on \
499+
other trait implementations to satisfy trait bounds"
500+
.to_string(),
501+
);
502+
call_graph.add_edge(f_node.clone(), expr_node.clone());
503+
call_graph.add_edge(expr_node.clone(), Node::TraitImpl(impl_path.clone()));
492504
}
493505

494506
// f --> f2

source/vir/src/recursive_types.rs

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,16 @@ fn check_well_founded_typ(
126126
pub(crate) enum TypNode {
127127
Datatype(Path),
128128
TraitImpl(Path),
129+
// This is used to replace an X --> Y edge with X --> SpanInfo --> Y edges
130+
// to give more precise span information than X or Y alone provide
131+
SpanInfo { span_infos_index: usize, text: String },
129132
}
130133

131134
struct CheckPositiveGlobal {
132135
krate: Krate,
133136
datatypes: HashMap<Path, Datatype>,
134137
type_graph: Graph<TypNode>,
138+
span_infos: Vec<Span>,
135139
}
136140

137141
struct CheckPositiveLocal {
@@ -140,6 +144,18 @@ struct CheckPositiveLocal {
140144
tparams: HashMap<Ident, AcceptRecursiveType>,
141145
}
142146

147+
pub(crate) fn new_span_info_node(span_infos: &mut Vec<Span>, span: Span, text: String) -> Node {
148+
let node = Node::SpanInfo { span_infos_index: span_infos.len(), text };
149+
span_infos.push(span);
150+
node
151+
}
152+
153+
fn new_span_info_typ_node(span_infos: &mut Vec<Span>, span: Span, text: String) -> TypNode {
154+
let node = TypNode::SpanInfo { span_infos_index: span_infos.len(), text };
155+
span_infos.push(span);
156+
node
157+
}
158+
143159
// polarity = Some(true) for positive, Some(false) for negative, None for neither
144160
fn check_positive_uses(
145161
global: &CheckPositiveGlobal,
@@ -208,7 +224,12 @@ fn check_positive_uses(
208224
if global.type_graph.in_same_scc(&impl_node, &my_node) {
209225
let scc_rep = global.type_graph.get_scc_rep(&my_node);
210226
let scc_nodes = global.type_graph.shortest_cycle_back_to_self(&scc_rep);
211-
return Err(type_scc_error(&global.krate, &my_node, &scc_nodes));
227+
return Err(type_scc_error(
228+
&global.krate,
229+
&global.span_infos,
230+
&my_node,
231+
&scc_nodes,
232+
));
212233
}
213234
}
214235
Ok(())
@@ -256,7 +277,7 @@ fn add_one_type_to_graph(type_graph: &mut Graph<TypNode>, src: &TypNode, typ: &T
256277
}
257278
}
258279

259-
pub(crate) fn build_datatype_graph(krate: &Krate) -> Graph<TypNode> {
280+
pub(crate) fn build_datatype_graph(krate: &Krate, span_infos: &mut Vec<Span>) -> Graph<TypNode> {
260281
let mut type_graph: Graph<TypNode> = Graph::new();
261282

262283
// If datatype D1 has a field whose type mentions datatype D2, create a graph edge D1 --> D2
@@ -270,7 +291,15 @@ pub(crate) fn build_datatype_graph(krate: &Krate) -> Graph<TypNode> {
270291
}
271292

272293
for a in &krate.assoc_type_impls {
273-
let src = TypNode::TraitImpl(a.x.impl_path.clone());
294+
let trait_impl_src = TypNode::TraitImpl(a.x.impl_path.clone());
295+
let src = new_span_info_typ_node(
296+
span_infos,
297+
a.span.clone(),
298+
": associated type definition, which may depend on other trait implementations \
299+
to satisfy type bounds"
300+
.to_string(),
301+
);
302+
type_graph.add_edge(trait_impl_src, src.clone());
274303
for impl_path in a.x.impl_paths.iter() {
275304
let dst = TypNode::TraitImpl(impl_path.clone());
276305
type_graph.add_edge(src.clone(), dst);
@@ -284,19 +313,21 @@ pub(crate) fn build_datatype_graph(krate: &Krate) -> Graph<TypNode> {
284313
}
285314

286315
type_graph.compute_sccs();
287-
return type_graph;
316+
317+
type_graph
288318
}
289319

290320
pub(crate) fn check_recursive_types(krate: &Krate) -> Result<(), VirErr> {
291-
let type_graph = build_datatype_graph(krate);
321+
let mut span_infos: Vec<Span> = Vec::new();
322+
let type_graph = build_datatype_graph(krate, &mut span_infos);
292323
let mut datatypes: HashMap<Path, Datatype> = HashMap::new();
293324
let mut datatypes_well_founded: HashSet<Path> = HashSet::new();
294325

295326
for datatype in &krate.datatypes {
296327
datatypes.insert(datatype.x.path.clone(), datatype.clone());
297328
}
298329

299-
let global = CheckPositiveGlobal { krate: krate.clone(), datatypes, type_graph };
330+
let global = CheckPositiveGlobal { krate: krate.clone(), datatypes, type_graph, span_infos };
300331

301332
for function in &krate.functions {
302333
if let FunctionKind::TraitMethodDecl { .. } = function.x.kind {
@@ -343,6 +374,7 @@ pub(crate) fn check_recursive_types(krate: &Krate) -> Result<(), VirErr> {
343374
TypNode::TraitImpl(_) if global.type_graph.node_is_in_cycle(node) => {
344375
return Err(type_scc_error(
345376
krate,
377+
&global.span_infos,
346378
node,
347379
&global.type_graph.shortest_cycle_back_to_self(scc),
348380
));
@@ -377,75 +409,96 @@ pub(crate) fn check_recursive_types(krate: &Krate) -> Result<(), VirErr> {
377409
Ok(())
378410
}
379411

380-
fn type_scc_error(krate: &Krate, head: &TypNode, nodes: &Vec<TypNode>) -> VirErr {
412+
fn type_scc_error(
413+
krate: &Krate,
414+
span_infos: &Vec<Span>,
415+
head: &TypNode,
416+
nodes: &Vec<TypNode>,
417+
) -> VirErr {
381418
let msg =
382419
"found a cyclic self-reference in a trait definition, which may result in nontermination"
383420
.to_string();
384421
let mut err = crate::messages::error_bare(msg);
385422
for (i, node) in nodes.iter().enumerate() {
386-
let mut push = |node: &TypNode, span: Span| {
423+
let mut push = |node: &TypNode, span: Span, text: &str| {
387424
if node == head {
388425
err = err.primary_span(&span);
389426
}
390-
let msg = format!("may be part of cycle (node {} of {} in cycle)", i + 1, nodes.len());
427+
let msg = format!(
428+
"may be part of cycle (node {} of {} in cycle){}",
429+
i + 1,
430+
nodes.len(),
431+
text
432+
);
391433
err = err.secondary_label(&span, msg);
392434
};
393435
match node {
394436
TypNode::Datatype(path) => {
395437
if let Some(d) = krate.datatypes.iter().find(|t| t.x.path == *path) {
396438
let span = d.span.clone();
397-
push(node, span);
439+
push(node, span, ": type definition");
398440
}
399441
}
400442
TypNode::TraitImpl(impl_path) => {
401443
if let Some(t) = krate.trait_impls.iter().find(|t| t.x.impl_path == *impl_path) {
402444
let span = t.span.clone();
403-
push(node, span);
445+
push(node, span, ": implementation of trait for a type");
404446
}
405447
}
448+
TypNode::SpanInfo { span_infos_index, text } => {
449+
push(node, span_infos[*span_infos_index].clone(), text);
450+
}
406451
}
407452
}
408453
err
409454
}
410455

411-
fn scc_error(krate: &Krate, head: &Node, nodes: &Vec<Node>) -> VirErr {
456+
fn scc_error(krate: &Krate, span_infos: &Vec<Span>, head: &Node, nodes: &Vec<Node>) -> VirErr {
412457
let msg =
413458
"found a cyclic self-reference in a trait definition, which may result in nontermination"
414459
.to_string();
415460
let mut err = crate::messages::error_bare(msg);
416461
for (i, node) in nodes.iter().enumerate() {
417-
let mut push = |node: &Node, span: Span| {
462+
let mut push = |node: &Node, span: Span, text: &str| {
418463
if node == head {
419464
err = err.primary_span(&span);
420465
}
421-
let msg = format!("may be part of cycle (node {} of {} in cycle)", i + 1, nodes.len());
466+
let msg = format!(
467+
"may be part of cycle (node {} of {} in cycle){}",
468+
i + 1,
469+
nodes.len(),
470+
text
471+
);
422472
err = err.secondary_label(&span, msg);
423473
};
424474
match node {
425475
Node::Fun(fun) => {
426476
if let Some(f) = krate.functions.iter().find(|f| f.x.name == *fun) {
427477
let span = f.span.clone();
428-
push(node, span);
478+
push(node, span, ": function definition, whose body may have dependencies");
429479
}
430480
}
431481
Node::Datatype(path) => {
432482
if let Some(d) = krate.datatypes.iter().find(|t| t.x.path == *path) {
433483
let span = d.span.clone();
434-
push(node, span);
484+
push(node, span, ": type definition");
435485
}
436486
}
437487
Node::Trait(trait_path) => {
438488
if let Some(t) = krate.traits.iter().find(|t| t.x.name == *trait_path) {
439489
let span = t.span.clone();
440-
push(node, span);
490+
push(node, span, ": declaration of trait");
441491
}
442492
}
443493
Node::TraitImpl(impl_path) => {
444494
if let Some(t) = krate.trait_impls.iter().find(|t| t.x.impl_path == *impl_path) {
445495
let span = t.span.clone();
446-
push(node, span);
496+
push(node, span, ": implementation of trait for a type");
447497
}
448498
}
499+
Node::SpanInfo { span_infos_index, text } => {
500+
push(node, span_infos[*span_infos_index].clone(), text);
501+
}
449502
}
450503
}
451504
err
@@ -495,14 +548,27 @@ pub(crate) fn add_trait_to_graph(call_graph: &mut Graph<Node>, trt: &Trait) {
495548
}
496549
}
497550

498-
pub(crate) fn add_trait_impl_to_graph(call_graph: &mut Graph<Node>, t: &crate::ast::TraitImpl) {
551+
pub(crate) fn add_trait_impl_to_graph(
552+
span_infos: &mut Vec<Span>,
553+
call_graph: &mut Graph<Node>,
554+
t: &crate::ast::TraitImpl,
555+
) {
499556
// For
500557
// trait T<...> where ...: U1(...), ..., ...: Un(...)
501558
// impl T<t1...tn> for ... { ... }
502559
// Add necessary impl_T_for_* --> impl_Ui_for_* edges
503560
// This corresponds to instantiating the a: Dictionary_U<A> field in the comments below
504-
let src_node = Node::TraitImpl(t.x.impl_path.clone());
505-
for imp in t.x.trait_typ_arg_impls.iter() {
561+
let trait_impl_src_node = Node::TraitImpl(t.x.impl_path.clone());
562+
let src_node = new_span_info_node(
563+
span_infos,
564+
t.x.trait_typ_arg_impls.span.clone(),
565+
": an implementation of a trait, applying the trait to some type arguments, \
566+
for some `Self` type, where applying the trait to type arguments and declaring \
567+
the `Self` type may depend on other trait implementations to satisfy type bounds"
568+
.to_string(),
569+
);
570+
call_graph.add_edge(trait_impl_src_node, src_node.clone());
571+
for imp in t.x.trait_typ_arg_impls.x.iter() {
506572
if &t.x.impl_path != imp {
507573
call_graph.add_edge(src_node.clone(), Node::TraitImpl(imp.clone()));
508574
}
@@ -648,6 +714,7 @@ pub fn check_traits(krate: &Krate, ctx: &GlobalCtx) -> Result<(), VirErr> {
648714
_ if ctx.func_call_graph.node_is_in_cycle(node) => {
649715
return Err(scc_error(
650716
krate,
717+
&ctx.datatype_graph_span_infos,
651718
node,
652719
&ctx.func_call_graph.shortest_cycle_back_to_self(node),
653720
));

0 commit comments

Comments
 (0)