From abe040d876f697ef86b1ad395caf68c03ecbf046 Mon Sep 17 00:00:00 2001 From: kadmin Date: Sun, 4 Dec 2022 19:58:03 +0000 Subject: [PATCH 1/5] Change commit_if_ok to probe --- .../src/traits/const_evaluatable.rs | 11 ++---- .../const_kind_expr/wf_obligation.rs | 3 +- .../const_kind_expr/wf_obligation.stderr | 12 +------ .../ui/const-generics/issues/issue-105037.rs | 35 +++++++++++++++++++ 4 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/const-generics/issues/issue-105037.rs diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index f8efe9bfa9f82..f449d360c1686 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -173,16 +173,11 @@ fn satisfied_from_param_env<'tcx>( type BreakTy = (); fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { debug!("is_const_evaluatable: candidate={:?}", c); - if let Ok(()) = self.infcx.commit_if_ok(|_| { + if self.infcx.probe(|_| { let ocx = ObligationCtxt::new_in_snapshot(self.infcx); - if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()) - && let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct) + ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok() + && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.select_all_or_error().is_empty() - { - Ok(()) - } else { - Err(()) - } }) { ControlFlow::BREAK } else if let ty::ConstKind::Expr(e) = c.kind() { diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs index 6093fc70b1696..b96e210808b97 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs +++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs @@ -15,8 +15,7 @@ where [(); (L - 1) + 1 + L]:, { foo::<_, L>([(); L + 1 + L]); - //~^ ERROR: mismatched types - //~^^ ERROR: unconstrained generic constant + //~^ ERROR: unconstrained generic constant } fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr index da5194696e657..09e5e3f862a20 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr @@ -1,12 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/wf_obligation.rs:17:17 - | -LL | foo::<_, L>([(); L + 1 + L]); - | ^^^^^^^^^^^^^^^ expected `N + 1 + M`, found `L + 1 + L` - | - = note: expected constant `N + 1 + M` - found constant `L + 1 + L` - error: unconstrained generic constant --> $DIR/wf_obligation.rs:17:22 | @@ -15,6 +6,5 @@ LL | foo::<_, L>([(); L + 1 + L]); | = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:` -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/issues/issue-105037.rs b/src/test/ui/const-generics/issues/issue-105037.rs new file mode 100644 index 0000000000000..f7d2394994396 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-105037.rs @@ -0,0 +1,35 @@ +// run-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] +#![allow(dead_code)] + +trait Table: Sync { + const COLUMNS: usize; +} + +struct Table1; +impl Table for Table1 { + const COLUMNS: usize = 123; +} + +struct Table2; +impl Table for Table2 { + const COLUMNS: usize = 456; +} + +fn process_table, const D: usize>(_table: T) +where + [(); T::COLUMNS]:, +{ +} + +fn process_all_tables() +where + [(); Table2::::COLUMNS]:, + [(); Table1::::COLUMNS]:, +{ + process_table(Table1::); + process_table(Table2::); +} + +fn main() {} From 77b61379b6a808f388b347044b2d37a8968a274e Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 13 Dec 2022 09:51:13 +0000 Subject: [PATCH 2/5] Change based on comments Instead of just switching to a probe, check for different matches, and see how many there are. If one, unify it, otherwise return true and let it be unified later. --- .../src/traits/const_evaluatable.rs | 33 +++++++++++++++---- .../const_kind_expr/wf_obligation.rs | 1 + .../const_kind_expr/wf_obligation.stderr | 12 ++++++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index f449d360c1686..4f0b5f5940246 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -168,7 +168,9 @@ fn satisfied_from_param_env<'tcx>( param_env: ty::ParamEnv<'tcx>, infcx: &'a InferCtxt<'tcx>, + single_match: Option, ()>>, } + impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> { type BreakTy = (); fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { @@ -179,7 +181,9 @@ fn satisfied_from_param_env<'tcx>( && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.select_all_or_error().is_empty() }) { - ControlFlow::BREAK + self.single_match = + if self.single_match.is_none() { Some(Ok(c)) } else { Some(Err(())) }; + ControlFlow::CONTINUE } else if let ty::ConstKind::Expr(e) = c.kind() { e.visit_with(self) } else { @@ -195,22 +199,37 @@ fn satisfied_from_param_env<'tcx>( } } + let mut single_match: Option, ()>> = None; + for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(ce) => { let b_ct = tcx.expand_abstract_consts(ce); - let mut v = Visitor { ct, infcx, param_env }; - let result = b_ct.visit_with(&mut v); - - if let ControlFlow::Break(()) = result { - debug!("is_const_evaluatable: yes"); - return true; + let mut v = Visitor { ct, infcx, param_env, single_match: None }; + let _ = b_ct.visit_with(&mut v); + if let Some(inner) = v.single_match { + single_match = if single_match.is_none() { Some(inner) } else { Some(Err(())) }; } } _ => {} // don't care } } + if let Some(c) = single_match { + if let Ok(c) = c { + let is_ok = infcx + .commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); + if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(()) } + }) + .is_ok(); + assert!(is_ok); + } + return true; + } + debug!("is_const_evaluatable: no"); false } diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs index b96e210808b97..d64468767eb45 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs +++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs @@ -16,6 +16,7 @@ where { foo::<_, L>([(); L + 1 + L]); //~^ ERROR: unconstrained generic constant + //~| ERROR: mismatched types } fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr index 09e5e3f862a20..da5194696e657 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr @@ -1,3 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/wf_obligation.rs:17:17 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ^^^^^^^^^^^^^^^ expected `N + 1 + M`, found `L + 1 + L` + | + = note: expected constant `N + 1 + M` + found constant `L + 1 + L` + error: unconstrained generic constant --> $DIR/wf_obligation.rs:17:22 | @@ -6,5 +15,6 @@ LL | foo::<_, L>([(); L + 1 + L]); | = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:` -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`. From b79a9a0900dd1d29d8bf49e73b5ac75acafd9c3b Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 20 Dec 2022 03:36:32 +0000 Subject: [PATCH 3/5] Set !const_evaluatable if ambig. and not inferred This prevents an ICE due to a value not actually being evaluatable later. --- .../src/traits/const_evaluatable.rs | 22 +++++++++---------- .../const-generics/ensure_is_evaluatable.rs | 20 +++++++++++++++++ .../ensure_is_evaluatable.stderr | 18 +++++++++++++++ 3 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/const-generics/ensure_is_evaluatable.rs create mode 100644 src/test/ui/const-generics/ensure_is_evaluatable.stderr diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 4f0b5f5940246..8530b96796f20 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -215,18 +215,16 @@ fn satisfied_from_param_env<'tcx>( } } - if let Some(c) = single_match { - if let Ok(c) = c { - let is_ok = infcx - .commit_if_ok(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); - assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); - assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); - if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(()) } - }) - .is_ok(); - assert!(is_ok); - } + if let Some(Ok(c)) = single_match { + let is_ok = infcx + .commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); + if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(()) } + }) + .is_ok(); + assert!(is_ok); return true; } diff --git a/src/test/ui/const-generics/ensure_is_evaluatable.rs b/src/test/ui/const-generics/ensure_is_evaluatable.rs new file mode 100644 index 0000000000000..1e8d8c3d3559b --- /dev/null +++ b/src/test/ui/const-generics/ensure_is_evaluatable.rs @@ -0,0 +1,20 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn foo() -> [(); N+2] +where + [(); N + 1]:, + [(); M + 1]:, +{ + bar() + //~^ ERROR: unconstrained +} + +fn bar() -> [(); N] +where + [(); N + 1]:, +{ + [(); N] +} + +fn main() {} diff --git a/src/test/ui/const-generics/ensure_is_evaluatable.stderr b/src/test/ui/const-generics/ensure_is_evaluatable.stderr new file mode 100644 index 0000000000000..bf6c35ad8fd86 --- /dev/null +++ b/src/test/ui/const-generics/ensure_is_evaluatable.stderr @@ -0,0 +1,18 @@ +error: unconstrained generic constant + --> $DIR/ensure_is_evaluatable.rs:9:5 + | +LL | bar() + | ^^^ + | + = help: try adding a `where` bound using this expression: `where [(); N + 1]:` +note: required by a bound in `bar` + --> $DIR/ensure_is_evaluatable.rs:15:10 + | +LL | fn bar() -> [(); N] + | --- required by a bound in this +LL | where +LL | [(); N + 1]:, + | ^^^^^ required by this bound in `bar` + +error: aborting due to previous error + From 7c5cb737357518c9a704994e007b32ed0da214f3 Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 20 Dec 2022 21:11:19 +0000 Subject: [PATCH 4/5] Check for duplicates --- .../src/traits/const_evaluatable.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 8530b96796f20..c4d5cf0309b9c 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -181,8 +181,11 @@ fn satisfied_from_param_env<'tcx>( && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.select_all_or_error().is_empty() }) { - self.single_match = - if self.single_match.is_none() { Some(Ok(c)) } else { Some(Err(())) }; + self.single_match = match self.single_match { + None => Some(Ok(c)), + Some(Ok(o)) if o == c => Some(Ok(c)), + Some(_) => Some(Err(())), + }; ControlFlow::CONTINUE } else if let ty::ConstKind::Expr(e) = c.kind() { e.visit_with(self) @@ -207,8 +210,17 @@ fn satisfied_from_param_env<'tcx>( let b_ct = tcx.expand_abstract_consts(ce); let mut v = Visitor { ct, infcx, param_env, single_match: None }; let _ = b_ct.visit_with(&mut v); + if let Some(inner) = v.single_match { - single_match = if single_match.is_none() { Some(inner) } else { Some(Err(())) }; + single_match = if let Ok(inner) = inner { + match single_match { + None => Some(Ok(inner)), + Some(Ok(prev)) if prev == inner => Some(Ok(prev)), + Some(_) => Some(Err(())), + } + } else { + Some(Err(())) + }; } } _ => {} // don't care From 21c5ffe008cce39bcd676ed197f691adbfbf7a2f Mon Sep 17 00:00:00 2001 From: kadmin Date: Wed, 21 Dec 2022 21:53:52 +0000 Subject: [PATCH 5/5] Clean up Simplify match statement Add multiple tests - 1 test for checking `N + 1 + 1` does not unify with `N+1` - 2 tests for checking that a function that uses two parameters only returns the parameter that is actually used. - Check exact repeat predicates --- .../src/traits/const_evaluatable.rs | 32 ++++++------------- .../fn_with_two_const_inputs.rs | 23 +++++++++++++ .../fn_with_two_const_inputs.stderr | 18 +++++++++++ .../fn_with_two_same_const_inputs.rs | 22 +++++++++++++ .../const_kind_expr/wf_obligation.rs | 4 +-- .../ui/const-generics/two_matching_preds.rs | 19 +++++++++++ .../const-generics/unify_with_nested_expr.rs | 18 +++++++++++ .../unify_with_nested_expr.stderr | 22 +++++++++++++ 8 files changed, 133 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/const-generics/fn_with_two_const_inputs.rs create mode 100644 src/test/ui/const-generics/fn_with_two_const_inputs.stderr create mode 100644 src/test/ui/const-generics/fn_with_two_same_const_inputs.rs create mode 100644 src/test/ui/const-generics/two_matching_preds.rs create mode 100644 src/test/ui/const-generics/unify_with_nested_expr.rs create mode 100644 src/test/ui/const-generics/unify_with_nested_expr.stderr diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index c4d5cf0309b9c..71fb6058cd2c5 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -186,8 +186,9 @@ fn satisfied_from_param_env<'tcx>( Some(Ok(o)) if o == c => Some(Ok(c)), Some(_) => Some(Err(())), }; - ControlFlow::CONTINUE - } else if let ty::ConstKind::Expr(e) = c.kind() { + } + + if let ty::ConstKind::Expr(e) = c.kind() { e.visit_with(self) } else { // FIXME(generic_const_exprs): This doesn't recurse into `>::ASSOC`'s substs. @@ -208,35 +209,20 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(ce) => { let b_ct = tcx.expand_abstract_consts(ce); - let mut v = Visitor { ct, infcx, param_env, single_match: None }; + let mut v = Visitor { ct, infcx, param_env, single_match }; let _ = b_ct.visit_with(&mut v); - if let Some(inner) = v.single_match { - single_match = if let Ok(inner) = inner { - match single_match { - None => Some(Ok(inner)), - Some(Ok(prev)) if prev == inner => Some(Ok(prev)), - Some(_) => Some(Err(())), - } - } else { - Some(Err(())) - }; - } + single_match = v.single_match; } _ => {} // don't care } } if let Some(Ok(c)) = single_match { - let is_ok = infcx - .commit_if_ok(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); - assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); - assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); - if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(()) } - }) - .is_ok(); - assert!(is_ok); + let ocx = ObligationCtxt::new(infcx); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); + assert!(ocx.select_all_or_error().is_empty()); return true; } diff --git a/src/test/ui/const-generics/fn_with_two_const_inputs.rs b/src/test/ui/const-generics/fn_with_two_const_inputs.rs new file mode 100644 index 0000000000000..0d6246a9f02c3 --- /dev/null +++ b/src/test/ui/const-generics/fn_with_two_const_inputs.rs @@ -0,0 +1,23 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +const fn both(_: usize, b: usize) -> usize { + b +} + +fn foo() -> [(); N + 2] +where + [(); both(N + 1, M + 1)]:, +{ + bar() + //~^ ERROR: unconstrained generic constant +} + +fn bar() -> [(); N] +where + [(); N + 1]:, +{ + [(); N] +} + +fn main() {} diff --git a/src/test/ui/const-generics/fn_with_two_const_inputs.stderr b/src/test/ui/const-generics/fn_with_two_const_inputs.stderr new file mode 100644 index 0000000000000..614e7e0d2fc20 --- /dev/null +++ b/src/test/ui/const-generics/fn_with_two_const_inputs.stderr @@ -0,0 +1,18 @@ +error: unconstrained generic constant + --> $DIR/fn_with_two_const_inputs.rs:12:5 + | +LL | bar() + | ^^^ + | + = help: try adding a `where` bound using this expression: `where [(); N + 1]:` +note: required by a bound in `bar` + --> $DIR/fn_with_two_const_inputs.rs:18:10 + | +LL | fn bar() -> [(); N] + | --- required by a bound in this +LL | where +LL | [(); N + 1]:, + | ^^^^^ required by this bound in `bar` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/fn_with_two_same_const_inputs.rs b/src/test/ui/const-generics/fn_with_two_same_const_inputs.rs new file mode 100644 index 0000000000000..f0ce093e07a4f --- /dev/null +++ b/src/test/ui/const-generics/fn_with_two_same_const_inputs.rs @@ -0,0 +1,22 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +const fn both(_: usize, b: usize) -> usize { + b +} + +fn foo() +where + [(); both(N + 1, N + 1)]:, +{ + bar::(); +} + +fn bar() +where + [(); N + 1]:, +{ +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs index d64468767eb45..6093fc70b1696 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs +++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs @@ -15,8 +15,8 @@ where [(); (L - 1) + 1 + L]:, { foo::<_, L>([(); L + 1 + L]); - //~^ ERROR: unconstrained generic constant - //~| ERROR: mismatched types + //~^ ERROR: mismatched types + //~^^ ERROR: unconstrained generic constant } fn main() {} diff --git a/src/test/ui/const-generics/two_matching_preds.rs b/src/test/ui/const-generics/two_matching_preds.rs new file mode 100644 index 0000000000000..de608f73e2c0c --- /dev/null +++ b/src/test/ui/const-generics/two_matching_preds.rs @@ -0,0 +1,19 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn foo() +where + [(); N + 1]:, + [(); N + 1]:, +{ + bar::(); +} + +fn bar() +where + [(); N + 1]:, +{ +} + +fn main() {} diff --git a/src/test/ui/const-generics/unify_with_nested_expr.rs b/src/test/ui/const-generics/unify_with_nested_expr.rs new file mode 100644 index 0000000000000..1271e0902a3bd --- /dev/null +++ b/src/test/ui/const-generics/unify_with_nested_expr.rs @@ -0,0 +1,18 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn foo() +where + [(); N + 1 + 1]:, +{ + bar(); + //~^ ERROR: type annotations +} + +fn bar() +where + [(); N + 1]:, +{ +} + +fn main() {} diff --git a/src/test/ui/const-generics/unify_with_nested_expr.stderr b/src/test/ui/const-generics/unify_with_nested_expr.stderr new file mode 100644 index 0000000000000..8bab0dff7f263 --- /dev/null +++ b/src/test/ui/const-generics/unify_with_nested_expr.stderr @@ -0,0 +1,22 @@ +error[E0284]: type annotations needed + --> $DIR/unify_with_nested_expr.rs:8:5 + | +LL | bar(); + | ^^^ cannot infer the value of the const parameter `N` declared on the function `bar` + | +note: required by a bound in `bar` + --> $DIR/unify_with_nested_expr.rs:14:10 + | +LL | fn bar() + | --- required by a bound in this +LL | where +LL | [(); N + 1]:, + | ^^^^^ required by this bound in `bar` +help: consider specifying the generic argument + | +LL | bar::(); + | +++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0284`.