Skip to content

Commit bc7b17c

Browse files
committed
Fix cycle when debug-printing opaque types
Fixes #61577 When printing an opaque type in non-verbose mode, we use the `tcx.predicates_of` query to retrieve the opaque type's bounds for pretty-printing. However, the pervasiveness of logging within librustc means that we may already be executing `tcx.predicates_of` for the opaque type we're trying to print, leading to a cycle error. This commit adds a new 'no queries' thread-local flag to the pretty printer. This flag is enabled during the computation of `predicates_of` for opaque types, and causes us to print the opaque type in 'verbose' mode (which does not require computing any additinal queries). This should only affect debug logging for highly nested log messages, not any user-visible output.
1 parent f1b882b commit bc7b17c

File tree

2 files changed

+95
-64
lines changed

2 files changed

+95
-64
lines changed

src/librustc/ty/print/pretty.rs

+65-38
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,24 @@ macro_rules! define_scoped_cx {
4949
thread_local! {
5050
static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false);
5151
static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false);
52+
static NO_QUERIES: Cell<bool> = Cell::new(false);
53+
}
54+
55+
/// Avoids running any queries during any prints that occur
56+
/// during the closure. This may alter the apperance of some
57+
/// types (e.g. forcing verbose printing for opaque types).
58+
/// This method is used during some queries (e.g. `predicates_of`
59+
/// for opaque types), to ensure that any debug printing that
60+
/// occurs during the query computation does not end up recursively
61+
/// calling the same query.
62+
pub fn with_no_queries<F: FnOnce() -> R, R>(f: F) -> R {
63+
NO_QUERIES.with(|no_queries| {
64+
let old = no_queries.get();
65+
no_queries.set(true);
66+
let result = f();
67+
no_queries.set(old);
68+
result
69+
})
5270
}
5371

5472
/// Force us to name impls with just the filename/line number. We
@@ -556,52 +574,61 @@ pub trait PrettyPrinter<'tcx>:
556574
}
557575
ty::Opaque(def_id, substs) => {
558576
// FIXME(eddyb) print this with `print_def_path`.
559-
if self.tcx().sess.verbose() {
577+
// We use verbose printing in 'NO_QUERIES' mode, to
578+
// avoid needing to call `predicates_of`. This should
579+
// only affect certain debug messages (e.g. messages printed
580+
// from `rustc::ty` during the computation of `tcx.predicates_of`),
581+
// and should have no effect on any compiler output.
582+
if self.tcx().sess.verbose() || NO_QUERIES.with(|q| q.get()) {
560583
p!(write("Opaque({:?}, {:?})", def_id, substs));
561584
return Ok(self);
562585
}
563586

564-
let def_key = self.tcx().def_key(def_id);
565-
if let Some(name) = def_key.disambiguated_data.data.get_opt_name() {
566-
p!(write("{}", name));
567-
let mut substs = substs.iter();
568-
// FIXME(eddyb) print this with `print_def_path`.
569-
if let Some(first) = substs.next() {
570-
p!(write("::<"));
571-
p!(print(first));
572-
for subst in substs {
573-
p!(write(", "), print(subst));
587+
return Ok(with_no_queries(|| {
588+
589+
let def_key = self.tcx().def_key(def_id);
590+
if let Some(name) = def_key.disambiguated_data.data.get_opt_name() {
591+
p!(write("{}", name));
592+
let mut substs = substs.iter();
593+
// FIXME(eddyb) print this with `print_def_path`.
594+
if let Some(first) = substs.next() {
595+
p!(write("::<"));
596+
p!(print(first));
597+
for subst in substs {
598+
p!(write(", "), print(subst));
599+
}
600+
p!(write(">"));
574601
}
575-
p!(write(">"));
602+
return Ok(self);
576603
}
577-
return Ok(self);
578-
}
579-
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
580-
// by looking up the projections associated with the def_id.
581-
let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
582-
583-
let mut first = true;
584-
let mut is_sized = false;
585-
p!(write("impl"));
586-
for predicate in bounds.predicates {
587-
if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() {
588-
// Don't print +Sized, but rather +?Sized if absent.
589-
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
590-
is_sized = true;
591-
continue;
592-
}
604+
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
605+
// by looking up the projections associated with the def_id.
606+
let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
607+
608+
let mut first = true;
609+
let mut is_sized = false;
610+
p!(write("impl"));
611+
for predicate in bounds.predicates {
612+
if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() {
613+
// Don't print +Sized, but rather +?Sized if absent.
614+
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
615+
is_sized = true;
616+
continue;
617+
}
593618

594-
p!(
595-
write("{}", if first { " " } else { "+" }),
596-
print(trait_ref));
597-
first = false;
619+
p!(
620+
write("{}", if first { " " } else { "+" }),
621+
print(trait_ref));
622+
first = false;
623+
}
598624
}
599-
}
600-
if !is_sized {
601-
p!(write("{}?Sized", if first { " " } else { "+" }));
602-
} else if first {
603-
p!(write(" Sized"));
604-
}
625+
if !is_sized {
626+
p!(write("{}?Sized", if first { " " } else { "+" }));
627+
} else if first {
628+
p!(write(" Sized"));
629+
}
630+
Ok(self)
631+
})?);
605632
}
606633
ty::Str => p!(write("str")),
607634
ty::Generator(did, substs, movability) => {

src/librustc_typeck/collect.rs

+30-26
Original file line numberDiff line numberDiff line change
@@ -2056,20 +2056,22 @@ fn explicit_predicates_of(
20562056

20572057
Node::ImplItem(item) => match item.kind {
20582058
ImplItemKind::OpaqueTy(ref bounds) => {
2059-
let substs = InternalSubsts::identity_for_item(tcx, def_id);
2060-
let opaque_ty = tcx.mk_opaque(def_id, substs);
2061-
2062-
// Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`.
2063-
let bounds = AstConv::compute_bounds(
2064-
&icx,
2065-
opaque_ty,
2066-
bounds,
2067-
SizedByDefault::Yes,
2068-
tcx.def_span(def_id),
2069-
);
2059+
ty::print::with_no_queries(|| {
2060+
let substs = InternalSubsts::identity_for_item(tcx, def_id);
2061+
let opaque_ty = tcx.mk_opaque(def_id, substs);
2062+
2063+
// Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`.
2064+
let bounds = AstConv::compute_bounds(
2065+
&icx,
2066+
opaque_ty,
2067+
bounds,
2068+
SizedByDefault::Yes,
2069+
tcx.def_span(def_id),
2070+
);
20702071

2071-
predicates.extend(bounds.predicates(tcx, opaque_ty));
2072-
&item.generics
2072+
predicates.extend(bounds.predicates(tcx, opaque_ty));
2073+
&item.generics
2074+
})
20732075
}
20742076
_ => &item.generics,
20752077
},
@@ -2102,19 +2104,21 @@ fn explicit_predicates_of(
21022104
ref generics,
21032105
origin: _,
21042106
}) => {
2105-
let substs = InternalSubsts::identity_for_item(tcx, def_id);
2106-
let opaque_ty = tcx.mk_opaque(def_id, substs);
2107-
2108-
// Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`.
2109-
let bounds = AstConv::compute_bounds(
2110-
&icx,
2111-
opaque_ty,
2112-
bounds,
2113-
SizedByDefault::Yes,
2114-
tcx.def_span(def_id),
2115-
);
2107+
let bounds_predicates = ty::print::with_no_queries(|| {
2108+
let substs = InternalSubsts::identity_for_item(tcx, def_id);
2109+
let opaque_ty = tcx.mk_opaque(def_id, substs);
2110+
2111+
// Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`.
2112+
let bounds = AstConv::compute_bounds(
2113+
&icx,
2114+
opaque_ty,
2115+
bounds,
2116+
SizedByDefault::Yes,
2117+
tcx.def_span(def_id),
2118+
);
21162119

2117-
let bounds_predicates = bounds.predicates(tcx, opaque_ty);
2120+
bounds.predicates(tcx, opaque_ty)
2121+
});
21182122
if impl_trait_fn.is_some() {
21192123
// opaque types
21202124
return ty::GenericPredicates {
@@ -2124,7 +2128,7 @@ fn explicit_predicates_of(
21242128
} else {
21252129
// named opaque types
21262130
predicates.extend(bounds_predicates);
2127-
generics
2131+
generics.clone()
21282132
}
21292133
}
21302134

0 commit comments

Comments
 (0)