From cb856112ec32da97b39a57137ff4571f079b12e9 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 19 May 2020 00:13:28 -0400 Subject: [PATCH] Avoid stack overflowing when printing unevaluated const Fixes #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. --- src/librustc_middle/ty/print/pretty.rs | 58 +++++++++++++------ .../ui/const-generics/auxiliary/impl-const.rs | 9 +++ .../issue-68104-print-stack-overflow.rs | 14 +++++ 3 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 src/test/ui/const-generics/auxiliary/impl-const.rs create mode 100644 src/test/ui/const-generics/issue-68104-print-stack-overflow.rs diff --git a/src/librustc_middle/ty/print/pretty.rs b/src/librustc_middle/ty/print/pretty.rs index 2502a4a13a8f0..bcecc8a9f5906 100644 --- a/src/librustc_middle/ty/print/pretty.rs +++ b/src/librustc_middle/ty/print/pretty.rs @@ -54,6 +54,7 @@ thread_local! { static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); static SHOULD_PREFIX_WITH_CRATE: Cell = Cell::new(false); static NO_QUERIES: Cell = Cell::new(false); + static NO_CONST_PATH: Cell = Cell::new(false); } /// Avoids running any queries during any prints that occur @@ -72,6 +73,15 @@ pub fn with_no_queries R, R>(f: F) -> R { }) } +pub fn with_no_const_path 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, @@ -858,7 +868,9 @@ pub trait PrettyPrinter<'tcx>: ) -> Result { 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); } @@ -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)), diff --git a/src/test/ui/const-generics/auxiliary/impl-const.rs b/src/test/ui/const-generics/auxiliary/impl-const.rs new file mode 100644 index 0000000000000..fc993d63927c3 --- /dev/null +++ b/src/test/ui/const-generics/auxiliary/impl-const.rs @@ -0,0 +1,9 @@ +#![feature(const_generics)] + +pub struct Num; + +// Braces around const expression causes crash +impl Num<{5}> { + pub fn five(&self) { + } +} diff --git a/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs new file mode 100644 index 0000000000000..bda9ce8767d08 --- /dev/null +++ b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs @@ -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(); +}