Skip to content

Commit

Permalink
Avoid stack overflowing when printing unevaluated const
Browse files Browse the repository at this point in the history
Fixes rust-lang#68104

When printing the DefPath for an unevaluated const, we may end up trying
to print the same const again. To avoid infinite recursion, we use the
'verbose' format when we try to re-entrantly print a const. This ensures
that the pretty-printing process will always terminate.
  • Loading branch information
Aaron1011 committed May 19, 2020
1 parent 89988fe commit cb85611
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 18 deletions.
58 changes: 40 additions & 18 deletions src/librustc_middle/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ thread_local! {
static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false);
static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false);
static NO_QUERIES: Cell<bool> = Cell::new(false);
static NO_CONST_PATH: Cell<bool> = Cell::new(false);
}

/// Avoids running any queries during any prints that occur
Expand All @@ -72,6 +73,15 @@ pub fn with_no_queries<F: FnOnce() -> R, R>(f: F) -> R {
})
}

pub fn with_no_const_path<F: FnOnce() -> R, R>(f: F) -> R {
NO_CONST_PATH.with(|no_const_path| {
let old = no_const_path.replace(true);
let result = f();
no_const_path.set(old);
result
})
}

/// Force us to name impls with just the filename/line number. We
/// normally try to use types. But at some points, notably while printing
/// cycle errors, this can result in extra or suboptimal error output,
Expand Down Expand Up @@ -858,7 +868,9 @@ pub trait PrettyPrinter<'tcx>:
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);

if self.tcx().sess.verbose() {
// See the call to `with_no_const_path` inside the
// `ty::ConstKind::Unevaluated` for why we check `NO_CONST_PATH`
if self.tcx().sess.verbose() || NO_CONST_PATH.with(|q| q.get()) {
p!(write("Const({:?}: {:?})", ct.val, ct.ty));
return Ok(self);
}
Expand All @@ -882,29 +894,39 @@ pub trait PrettyPrinter<'tcx>:

match ct.val {
ty::ConstKind::Unevaluated(did, substs, promoted) => {
if let Some(promoted) = promoted {
p!(print_value_path(did, substs));
p!(write("::{:?}", promoted));
} else {
match self.tcx().def_kind(did) {
DefKind::Static | DefKind::Const | DefKind::AssocConst => {
p!(print_value_path(did, substs))
}
_ => {
if did.is_local() {
let span = self.tcx().def_span(did);
if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span)
{
p!(write("{}", snip))
// Don't re-entrantly enter this code path: that is,
// don't try to print the DefPath of an unevaluated const
// while we're already printing the DefPath of an unevaluated
// const. This ensures that we never end up trying to recursively
// print a const that we're already printing.
// See issue #68104 for more details
self = with_no_const_path(|| {
if let Some(promoted) = promoted {
p!(print_value_path(did, substs));
p!(write("::{:?}", promoted));
} else {
match self.tcx().def_kind(did) {
DefKind::Static | DefKind::Const | DefKind::AssocConst => {
p!(print_value_path(did, substs))
}
_ => {
if did.is_local() {
let span = self.tcx().def_span(did);
if let Ok(snip) =
self.tcx().sess.source_map().span_to_snippet(span)
{
p!(write("{}", snip))
} else {
print_underscore!()
}
} else {
print_underscore!()
}
} else {
print_underscore!()
}
}
}
}
Ok(self)
})?;
}
ty::ConstKind::Infer(..) => print_underscore!(),
ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/const-generics/auxiliary/impl-const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![feature(const_generics)]

pub struct Num<const N: usize>;

// Braces around const expression causes crash
impl Num<{5}> {
pub fn five(&self) {
}
}
14 changes: 14 additions & 0 deletions src/test/ui/const-generics/issue-68104-print-stack-overflow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// aux-build:impl-const.rs
// run-pass

#![feature(const_generics)]
#![allow(incomplete_features)]

extern crate impl_const;

use impl_const::*;

pub fn main() {
let n = Num::<5>;
n.five();
}

0 comments on commit cb85611

Please sign in to comment.