diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs
index e16756651e9..c9765ca1946 100644
--- a/compiler/noirc_frontend/src/parser/parser.rs
+++ b/compiler/noirc_frontend/src/parser/parser.rs
@@ -789,7 +789,7 @@ where
 }
 
 fn fresh_statement() -> impl NoirParser<StatementKind> {
-    statement(expression(), expression_no_constructors())
+    statement(expression(), expression_no_constructors(expression()))
 }
 
 fn constrain<'a, P>(expr_parser: P) -> impl NoirParser<StatementKind> + 'a
@@ -1189,8 +1189,8 @@ fn expression() -> impl ExprParser {
         expression_with_precedence(
             Precedence::Lowest,
             expr.clone(),
-            expression_no_constructors(),
-            statement(expr, expression_no_constructors()),
+            expression_no_constructors(expr.clone()),
+            statement(expr.clone(), expression_no_constructors(expr)),
             false,
             true,
         )
@@ -1198,13 +1198,16 @@ fn expression() -> impl ExprParser {
     .labelled(ParsingRuleLabel::Expression)
 }
 
-fn expression_no_constructors() -> impl ExprParser {
-    recursive(|expr| {
+fn expression_no_constructors<'a, P>(expr_parser: P) -> impl ExprParser + 'a
+where
+    P: ExprParser + 'a,
+{
+    recursive(|expr_no_constructors| {
         expression_with_precedence(
             Precedence::Lowest,
-            expr.clone(),
-            expr.clone(),
-            statement(expr.clone(), expr),
+            expr_parser.clone(),
+            expr_no_constructors.clone(),
+            statement(expr_parser, expr_no_constructors),
             false,
             false,
         )
@@ -1772,7 +1775,7 @@ mod test {
         parse_all(
             atom_or_right_unary(
                 expression(),
-                expression_no_constructors(),
+                expression_no_constructors(expression()),
                 fresh_statement(),
                 true,
             ),
@@ -1781,7 +1784,7 @@ mod test {
         parse_all_failing(
             atom_or_right_unary(
                 expression(),
-                expression_no_constructors(),
+                expression_no_constructors(expression()),
                 fresh_statement(),
                 true,
             ),
@@ -1801,7 +1804,7 @@ mod test {
         parse_all(
             atom_or_right_unary(
                 expression(),
-                expression_no_constructors(),
+                expression_no_constructors(expression()),
                 fresh_statement(),
                 true,
             ),
@@ -2040,12 +2043,12 @@ mod test {
     #[test]
     fn parse_for_loop() {
         parse_all(
-            for_loop(expression_no_constructors(), fresh_statement()),
+            for_loop(expression_no_constructors(expression()), fresh_statement()),
             vec!["for i in x+y..z {}", "for i in 0..100 { foo; bar }"],
         );
 
         parse_all_failing(
-            for_loop(expression_no_constructors(), fresh_statement()),
+            for_loop(expression_no_constructors(expression()), fresh_statement()),
             vec![
                 "for 1 in x+y..z {}",  // Cannot have a literal as the loop identifier
                 "for i in 0...100 {}", // Only '..' is supported, there are no inclusive ranges yet
@@ -2140,11 +2143,11 @@ mod test {
     #[test]
     fn parse_parenthesized_expression() {
         parse_all(
-            atom(expression(), expression_no_constructors(), fresh_statement(), true),
+            atom(expression(), expression_no_constructors(expression()), fresh_statement(), true),
             vec!["(0)", "(x+a)", "({(({{({(nested)})}}))})"],
         );
         parse_all_failing(
-            atom(expression(), expression_no_constructors(), fresh_statement(), true),
+            atom(expression(), expression_no_constructors(expression()), fresh_statement(), true),
             vec!["(x+a", "((x+a)", "(,)"],
         );
     }
@@ -2157,12 +2160,12 @@ mod test {
     #[test]
     fn parse_if_expr() {
         parse_all(
-            if_expr(expression_no_constructors(), fresh_statement()),
+            if_expr(expression_no_constructors(expression()), fresh_statement()),
             vec!["if x + a {  } else {  }", "if x {}", "if x {} else if y {} else {}"],
         );
 
         parse_all_failing(
-            if_expr(expression_no_constructors(), fresh_statement()),
+            if_expr(expression_no_constructors(expression()), fresh_statement()),
             vec!["if (x / a) + 1 {} else", "if foo then 1 else 2", "if true { 1 }else 3"],
         );
     }
@@ -2256,11 +2259,11 @@ mod test {
     #[test]
     fn parse_unary() {
         parse_all(
-            term(expression(), expression_no_constructors(), fresh_statement(), true),
+            term(expression(), expression_no_constructors(expression()), fresh_statement(), true),
             vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"],
         );
         parse_all_failing(
-            term(expression(), expression_no_constructors(), fresh_statement(), true),
+            term(expression(), expression_no_constructors(expression()), fresh_statement(), true),
             vec!["+hello", "/hello"],
         );
     }
@@ -2438,4 +2441,52 @@ mod test {
 
         assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),);
     }
+
+    #[test]
+    fn expr_no_constructors() {
+        let cases = vec![
+            (
+                "{ if structure { a: 1 } {} }",
+                1,
+                "{\n    if plain::structure {\n        Error\n    }\n    {\n    }\n}",
+            ),
+            (
+                "{ if ( structure { a: 1 } ) {} }",
+                0,
+                "{\n    if ((plain::structure { a: 1 })) {\n    }\n}",
+            ),
+            ("{ if ( structure {} ) {} }", 0, "{\n    if ((plain::structure {  })) {\n    }\n}"),
+            (
+                "{ if (a { x: 1 }, b { y: 2 }) {} }",
+                0,
+                "{\n    if ((plain::a { x: 1 }), (plain::b { y: 2 })) {\n    }\n}",
+            ),
+            (
+                "{ if ({ let foo = bar { baz: 42 }; foo == bar { baz: 42 }}) {} }",
+                0,
+                "{\n    if ({\n        let foo: unspecified = (plain::bar { baz: 42 })\
+                \n        (plain::foo == (plain::bar { baz: 42 }))\n    }) {\n    }\n}",
+            ),
+        ];
+
+        let show_errors = |v| vecmap(&v, ToString::to_string).join("\n");
+
+        let results = vecmap(&cases, |&(src, expected_errors, expected_result)| {
+            let (opt, errors) = parse_recover(block(fresh_statement()), src);
+            let actual = opt.map(|ast| ast.to_string());
+            let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() };
+
+            let result =
+                ((errors.len(), actual.clone()), (expected_errors, expected_result.to_string()));
+            if result.0 != result.1 {
+                let num_errors = errors.len();
+                let shown_errors = show_errors(errors);
+                eprintln!(
+                    "\nExpected {expected_errors} error(s) and got {num_errors}:\n\n{shown_errors}\n\nFrom input:   {src}\nExpected AST: {expected_result}\nActual AST:   {actual}\n");
+            }
+            result
+        });
+
+        assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),);
+    }
 }