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(); +}