Skip to content

Commit f96e3e2

Browse files
authored
Rollup merge of #72540 - davidtwco:issue-67552-mono-collector-comparison, r=varkor
mir: adjust conditional in recursion limit check Fixes #67552. This PR adjusts the condition used in the recursion limit check of the monomorphization collector, from `>` to `>=`. In #67552, the test case had infinite indirect recursion, repeating a handful of functions (from the perspective of the monomorphization collector): `rec` -> `identity` -> `Iterator::count` -> `Iterator::fold` -> `Iterator::next` -> `rec`. During this process, `resolve_associated_item` was invoked for `Iterator::fold` (during the construction of an `Instance`), and ICE'd due to substitutions needing inference. However, previous iterations of this recursion would have called this function for `Iterator::fold` - and did! - and succeeded in doing so (trivially checkable from debug logging, `()` is present where `_` is in the substs of the failing execution). The expected outcome of this test case would be a recursion limit error (which is present when the `identity` fn indirection is removed), and the recursion depth of `rec` is increasing (other functions finish collecting their neighbours and thus have their recursion depths reset). When the ICE occurs, the recursion depth of `rec` is 256 (which matches the recursion limit), which suggests perhaps that a different part of the compiler is using a `>=` comparison and returning a different result on this recursion rather than what it returned in every previous recursion, thus stopping the monomorphization collector from reporting an error on the next recursion, where `recursion_depth_of_rec > 256` would have been true. With grep and some educated guesses, we can determine that the recursion limit check at line 818 in `src/librustc_trait_selection/traits/project.rs` is the other check that is using a different comparison. Modifying either comparison to be `>` or `>=` respectively will fix the error, but changing the monomorphization collector produces the nicer error.
2 parents 43ae54d + a54ed87 commit f96e3e2

30 files changed

+147
-55
lines changed

src/librustc_error_codes/error_codes/E0055.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ recursion limit (which can be set via the `recursion_limit` attribute).
66
For a somewhat artificial example:
77

88
```compile_fail,E0055
9-
#![recursion_limit="5"]
9+
#![recursion_limit="4"]
1010
1111
struct Foo;
1212

src/librustc_expand/base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashMap;
1212
use rustc_data_structures::sync::{self, Lrc};
1313
use rustc_errors::{DiagnosticBuilder, ErrorReported};
1414
use rustc_parse::{self, parser, MACRO_ARGUMENTS};
15-
use rustc_session::parse::ParseSess;
15+
use rustc_session::{parse::ParseSess, Limit};
1616
use rustc_span::def_id::DefId;
1717
use rustc_span::edition::Edition;
1818
use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
@@ -941,7 +941,7 @@ pub struct ExpansionData {
941941
pub struct ExtCtxt<'a> {
942942
pub parse_sess: &'a ParseSess,
943943
pub ecfg: expand::ExpansionConfig<'a>,
944-
pub reduced_recursion_limit: Option<usize>,
944+
pub reduced_recursion_limit: Option<Limit>,
945945
pub root_path: PathBuf,
946946
pub resolver: &'a mut dyn Resolver,
947947
pub current_expansion: ExpansionData,

src/librustc_expand/expand.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_parse::validate_attr;
2424
use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
2525
use rustc_session::lint::BuiltinLintDiagnostics;
2626
use rustc_session::parse::{feature_err, ParseSess};
27+
use rustc_session::Limit;
2728
use rustc_span::source_map::respan;
2829
use rustc_span::symbol::{sym, Ident, Symbol};
2930
use rustc_span::{FileName, Span, DUMMY_SP};
@@ -664,7 +665,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
664665
) -> ExpandResult<AstFragment, Invocation> {
665666
let recursion_limit =
666667
self.cx.reduced_recursion_limit.unwrap_or(self.cx.ecfg.recursion_limit);
667-
if self.cx.current_expansion.depth > recursion_limit {
668+
if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) {
668669
if self.cx.reduced_recursion_limit.is_none() {
669670
self.error_recursion_limit_reached();
670671
}
@@ -1784,7 +1785,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
17841785
pub struct ExpansionConfig<'feat> {
17851786
pub crate_name: String,
17861787
pub features: Option<&'feat Features>,
1787-
pub recursion_limit: usize,
1788+
pub recursion_limit: Limit,
17881789
pub trace_mac: bool,
17891790
pub should_test: bool, // If false, strip `#[test]` nodes
17901791
pub keep_macs: bool,
@@ -1795,7 +1796,7 @@ impl<'feat> ExpansionConfig<'feat> {
17951796
ExpansionConfig {
17961797
crate_name,
17971798
features: None,
1798-
recursion_limit: 1024,
1799+
recursion_limit: Limit::new(1024),
17991800
trace_mac: false,
18001801
should_test: false,
18011802
keep_macs: false,

src/librustc_middle/middle/limits.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use crate::bug;
99
use rustc_ast::ast;
1010
use rustc_data_structures::sync::OnceCell;
11-
use rustc_session::Session;
11+
use rustc_session::{Limit, Session};
1212
use rustc_span::symbol::{sym, Symbol};
1313

1414
use std::num::IntErrorKind;
@@ -22,7 +22,7 @@ pub fn update_limits(sess: &Session, krate: &ast::Crate) {
2222
fn update_limit(
2323
sess: &Session,
2424
krate: &ast::Crate,
25-
limit: &OnceCell<usize>,
25+
limit: &OnceCell<Limit>,
2626
name: Symbol,
2727
default: usize,
2828
) {
@@ -34,7 +34,7 @@ fn update_limit(
3434
if let Some(s) = attr.value_str() {
3535
match s.as_str().parse() {
3636
Ok(n) => {
37-
limit.set(n).unwrap();
37+
limit.set(Limit::new(n)).unwrap();
3838
return;
3939
}
4040
Err(e) => {
@@ -62,5 +62,5 @@ fn update_limit(
6262
}
6363
}
6464
}
65-
limit.set(default).unwrap();
65+
limit.set(Limit::new(default)).unwrap();
6666
}

src/librustc_middle/ty/layout.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,9 @@ fn layout_raw<'tcx>(
187187
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
188188
) -> Result<&'tcx Layout, LayoutError<'tcx>> {
189189
ty::tls::with_related_context(tcx, move |icx| {
190-
let rec_limit = tcx.sess.recursion_limit.get().copied().unwrap();
191190
let (param_env, ty) = query.into_parts();
192191

193-
if icx.layout_depth > rec_limit {
192+
if !tcx.sess.recursion_limit().value_within_limit(icx.layout_depth) {
194193
tcx.sess.fatal(&format!("overflow representing the type `{}`", ty));
195194
}
196195

src/librustc_mir/const_eval/machine.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_data_structures::fx::FxHashMap;
1010
use rustc_ast::ast::Mutability;
1111
use rustc_hir::def_id::DefId;
1212
use rustc_middle::mir::AssertMessage;
13+
use rustc_session::Limit;
1314
use rustc_span::symbol::Symbol;
1415

1516
use crate::interpret::{
@@ -109,8 +110,8 @@ pub struct MemoryExtra {
109110
}
110111

111112
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
112-
pub(super) fn new(const_eval_limit: usize) -> Self {
113-
CompileTimeInterpreter { steps_remaining: const_eval_limit, stack: Vec::new() }
113+
pub(super) fn new(const_eval_limit: Limit) -> Self {
114+
CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new() }
114115
}
115116
}
116117

src/librustc_mir/interpret/eval_context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
651651
M::after_stack_push(self)?;
652652
info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance);
653653

654-
if self.stack().len() > self.tcx.sess.recursion_limit() {
654+
if !self.tcx.sess.recursion_limit().value_within_limit(self.stack().len()) {
655655
throw_exhaust!(StackFrameLimitReached)
656656
} else {
657657
Ok(())

src/librustc_mir/monomorphize/collector.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ fn check_recursion_limit<'tcx>(
430430
// Code that needs to instantiate the same function recursively
431431
// more than the recursion limit is assumed to be causing an
432432
// infinite expansion.
433-
if adjusted_recursion_depth > tcx.sess.recursion_limit() {
433+
if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) {
434434
let error = format!("reached the recursion limit while instantiating `{}`", instance);
435435
if let Some(def_id) = def_id.as_local() {
436436
let hir_id = tcx.hir().as_local_hir_id(def_id);
@@ -463,7 +463,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
463463
// which means that rustc basically hangs.
464464
//
465465
// Bail out in these cases to avoid that bad user experience.
466-
if type_length > tcx.sess.type_length_limit() {
466+
if !tcx.sess.type_length_limit().value_within_limit(type_length) {
467467
// The instance name is already known to be too long for rustc.
468468
// Show only the first and last 32 characters to avoid blasting
469469
// the user's terminal with thousands of lines of type-name.

src/librustc_session/session.rs

+48-6
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ use rustc_target::spec::{Target, TargetTriple, TlsModel};
2929

3030
use std::cell::{self, RefCell};
3131
use std::env;
32+
use std::fmt;
3233
use std::io::Write;
3334
use std::num::NonZeroU32;
35+
use std::ops::{Div, Mul};
3436
use std::path::PathBuf;
3537
use std::str::FromStr;
3638
use std::sync::Arc;
@@ -55,6 +57,46 @@ pub enum CtfeBacktrace {
5557
Immediate,
5658
}
5759

60+
/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against
61+
/// limits are consistent throughout the compiler.
62+
#[derive(Clone, Copy, Debug)]
63+
pub struct Limit(pub usize);
64+
65+
impl Limit {
66+
/// Create a new limit from a `usize`.
67+
pub fn new(value: usize) -> Self {
68+
Limit(value)
69+
}
70+
71+
/// Check that `value` is within the limit. Ensures that the same comparisons are used
72+
/// throughout the compiler, as mismatches can cause ICEs, see #72540.
73+
pub fn value_within_limit(&self, value: usize) -> bool {
74+
value <= self.0
75+
}
76+
}
77+
78+
impl fmt::Display for Limit {
79+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80+
write!(f, "{}", self.0)
81+
}
82+
}
83+
84+
impl Div<usize> for Limit {
85+
type Output = Limit;
86+
87+
fn div(self, rhs: usize) -> Self::Output {
88+
Limit::new(self.0 / rhs)
89+
}
90+
}
91+
92+
impl Mul<usize> for Limit {
93+
type Output = Limit;
94+
95+
fn mul(self, rhs: usize) -> Self::Output {
96+
Limit::new(self.0 * rhs)
97+
}
98+
}
99+
58100
/// Represents the data associated with a compilation
59101
/// session for a single crate.
60102
pub struct Session {
@@ -89,13 +131,13 @@ pub struct Session {
89131

90132
/// The maximum recursion limit for potentially infinitely recursive
91133
/// operations such as auto-dereference and monomorphization.
92-
pub recursion_limit: OnceCell<usize>,
134+
pub recursion_limit: OnceCell<Limit>,
93135

94136
/// The maximum length of types during monomorphization.
95-
pub type_length_limit: OnceCell<usize>,
137+
pub type_length_limit: OnceCell<Limit>,
96138

97139
/// The maximum blocks a const expression can evaluate.
98-
pub const_eval_limit: OnceCell<usize>,
140+
pub const_eval_limit: OnceCell<Limit>,
99141

100142
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
101143
/// Used for incremental compilation tests. Will only be populated if
@@ -255,15 +297,15 @@ impl Session {
255297
self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
256298
}
257299

258-
pub fn recursion_limit(&self) -> usize {
300+
pub fn recursion_limit(&self) -> Limit {
259301
self.recursion_limit.get().copied().unwrap()
260302
}
261303

262-
pub fn type_length_limit(&self) -> usize {
304+
pub fn type_length_limit(&self) -> Limit {
263305
self.type_length_limit.get().copied().unwrap()
264306
}
265307

266-
pub fn const_eval_limit(&self) -> usize {
308+
pub fn const_eval_limit(&self) -> Limit {
267309
self.const_eval_limit.get().copied().unwrap()
268310
}
269311

src/librustc_trait_selection/traits/project.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,10 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
333333

334334
Reveal::All => {
335335
let recursion_limit = self.tcx().sess.recursion_limit();
336-
if self.depth >= recursion_limit {
336+
if !recursion_limit.value_within_limit(self.depth) {
337337
let obligation = Obligation::with_depth(
338338
self.cause.clone(),
339-
recursion_limit,
339+
recursion_limit.0,
340340
self.param_env,
341341
ty,
342342
);
@@ -522,7 +522,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
522522
// But for now, let's classify this as an overflow:
523523
let recursion_limit = selcx.tcx().sess.recursion_limit();
524524
let obligation =
525-
Obligation::with_depth(cause, recursion_limit, param_env, projection_ty);
525+
Obligation::with_depth(cause, recursion_limit.0, param_env, projection_ty);
526526
selcx.infcx().report_overflow_error(&obligation, false);
527527
}
528528
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
@@ -814,8 +814,7 @@ fn project_type<'cx, 'tcx>(
814814
) -> Result<ProjectedTy<'tcx>, ProjectionTyError<'tcx>> {
815815
debug!("project(obligation={:?})", obligation);
816816

817-
let recursion_limit = selcx.tcx().sess.recursion_limit();
818-
if obligation.recursion_depth >= recursion_limit {
817+
if !selcx.tcx().sess.recursion_limit().value_within_limit(obligation.recursion_depth) {
819818
debug!("project: overflow!");
820819
return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow));
821820
}

src/librustc_trait_selection/traits/query/normalize.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
109109

110110
Reveal::All => {
111111
let recursion_limit = self.tcx().sess.recursion_limit();
112-
if self.anon_depth >= recursion_limit {
112+
if !recursion_limit.value_within_limit(self.anon_depth) {
113113
let obligation = Obligation::with_depth(
114114
self.cause.clone(),
115-
recursion_limit,
115+
recursion_limit.0,
116116
self.param_env,
117117
ty,
118118
);

src/librustc_trait_selection/traits/select.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
919919
obligation: &Obligation<'tcx, T>,
920920
error_obligation: &Obligation<'tcx, V>,
921921
) -> Result<(), OverflowError> {
922-
if obligation.recursion_depth >= self.infcx.tcx.sess.recursion_limit() {
922+
if !self.infcx.tcx.sess.recursion_limit().value_within_limit(obligation.recursion_depth) {
923923
match self.query_mode {
924924
TraitQueryMode::Standard => {
925925
self.infcx().report_overflow_error(error_obligation, true);

src/librustc_traits/dropck_outlives.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ fn dtorck_constraint_for_ty<'tcx>(
163163
) -> Result<(), NoSolution> {
164164
debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty);
165165

166-
if depth >= tcx.sess.recursion_limit() {
166+
if !tcx.sess.recursion_limit().value_within_limit(depth) {
167167
constraints.overflows.push(ty);
168168
return Ok(());
169169
}

src/librustc_ty/needs_drop.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_hir::def_id::DefId;
55
use rustc_middle::ty::subst::Subst;
66
use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop};
77
use rustc_middle::ty::{self, Ty, TyCtxt};
8+
use rustc_session::Limit;
89
use rustc_span::DUMMY_SP;
910

1011
type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
@@ -30,7 +31,7 @@ struct NeedsDropTypes<'tcx, F> {
3031
/// if it needs drop. If the result depends on whether some other types
3132
/// need drop we push them onto the stack.
3233
unchecked_tys: Vec<(Ty<'tcx>, usize)>,
33-
recursion_limit: usize,
34+
recursion_limit: Limit,
3435
adt_components: F,
3536
}
3637

@@ -66,7 +67,7 @@ where
6667
let tcx = self.tcx;
6768

6869
while let Some((ty, level)) = self.unchecked_tys.pop() {
69-
if level > self.recursion_limit {
70+
if !self.recursion_limit.value_within_limit(level) {
7071
// Not having a `Span` isn't great. But there's hopefully some other
7172
// recursion limit error as well.
7273
tcx.sess.span_err(

src/librustc_typeck/check/autoderef.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
4848
return Some((self.cur_ty, 0));
4949
}
5050

51-
if self.steps.len() >= tcx.sess.recursion_limit() {
51+
if !tcx.sess.recursion_limit().value_within_limit(self.steps.len()) {
5252
if !self.silence_errors {
5353
report_autoderef_recursion_limit_error(tcx, self.span, self.cur_ty);
5454
}

src/test/ui/did_you_mean/recursion_limit.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0275]: overflow evaluating the requirement `J: std::marker::Send`
1+
error[E0275]: overflow evaluating the requirement `K: std::marker::Send`
22
--> $DIR/recursion_limit.rs:34:5
33
|
44
LL | fn is_send<T:Send>() { }
@@ -8,6 +8,7 @@ LL | is_send::<A>();
88
| ^^^^^^^^^^^^
99
|
1010
= help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit`)
11+
= note: required because it appears within the type `J`
1112
= note: required because it appears within the type `I`
1213
= note: required because it appears within the type `H`
1314
= note: required because it appears within the type `G`

src/test/ui/did_you_mean/recursion_limit_deref.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0055]: reached the recursion limit while auto-dereferencing `I`
1+
error[E0055]: reached the recursion limit while auto-dereferencing `J`
22
--> $DIR/recursion_limit_deref.rs:50:22
33
|
44
LL | let x: &Bottom = &t;

src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ error[E0320]: overflow while adding drop-check rules for FingerTree<i32>
44
LL | let ft =
55
| ^^
66
|
7-
= note: overflowed on FingerTree<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<i32>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
7+
= note: overflowed on FingerTree<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<i32>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
88

99
error[E0320]: overflow while adding drop-check rules for FingerTree<i32>
1010
--> $DIR/dropck_no_diverge_on_nonregular_1.rs:25:9
1111
|
1212
LL | FingerTree::Single(1);
1313
| ^^^^^^^^^^^^^^^^^^^^^
1414
|
15-
= note: overflowed on FingerTree<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<i32>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
15+
= note: overflowed on FingerTree<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<Node<i32>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1616

1717
error: aborting due to 2 previous errors
1818

0 commit comments

Comments
 (0)