llvm / llvm/llvm-project

Infinite Loop During Parser Error Recovery in `clang-repl` After Malformed Statement Inside Brace Scope, related to Issue #206670

Open
#206,808 2 comments 0 reactions 0 assignees View on GitHub
clang-repl
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

# Infinite Loop During Parser Error Recovery in `clang-repl` After Malformed Statement Inside Brace Scope

## Summary

While investigating and fixing **Issue #206670**, an unrelated parser recovery bug was uncovered in `clang-repl`.

Once the original assertion failure is fixed, malformed statements inside any brace-enclosed scope no longer crash the interpreter. Instead, the parser enters an infinite recovery loop, consuming 100% CPU and never terminating.

This issue appears to be independent of namespaces and affects general parser error recovery.

---

# Reproducer

Start `clang-repl` and enter either of the following:

```cpp
namespace v1 {
int x = }
```

or

```cpp
{
int x = }
```

The same behavior can also be reproduced in non-interactive mode:

```bash
./build/bin/clang-repl "namespace v1 { int x = }"
```

---

# Expected Behavior

The interpreter should:

1. Emit the appropriate syntax diagnostics.
2. Recover from the malformed declaration.
3. Return control to the REPL (or terminate normally in non-interactive mode).

---

# Actual Behavior

Instead:

- syntax diagnostics are emitted,
- the parser never finishes recovering,
- the process enters an infinite loop,
- CPU usage reaches 100%,
- the process must be terminated manually (e.g. via `Ctrl+C`).

Eventually, Clang's diagnostic engine reaches the configured error limit and suppresses further diagnostics, but parsing continues indefinitely.

---

# Root Cause Analysis

The parser enters error recovery after encountering the malformed declaration.

During recovery, it attempts to skip tokens until reaching a synchronization point (typically a semicolon).

In this scenario, recovery appears to consume past the closing brace (`}`) of the active scope.

Once that happens:

- the parser loses track of the compound statement,
- it believes it is still inside an unfinished block,
- the current token stream is never advanced to a valid recovery point,
- and the same recovery path is repeatedly executed.

As a result, the parser loops forever without making forward progress.

---

# Observed Call Stack

Using `gdb`, the execution repeatedly remained in the same parser recovery path:

```text
(gdb) bt
#0 clang::Sema::DiscardCleanupsInEvaluationContext()
#1 clang::Sema::ActOnExprStmtError()
#2 clang::Parser::ParseExprStatement()
#3 clang::Parser::ParseCompoundStatementBody()
#4 clang::Parser::ParseStatementOrDeclaration()
```

The stack remains effectively identical across multiple iterations, suggesting the parser is repeatedly re-entering the same recovery logic without consuming additional tokens.

---

# Additional Notes

This issue was discovered while validating the fix for **Issue #206670**.

The original assertion masked this behavior by aborting before parser recovery could begin.

Once the assertion was removed, the parser progressed further and exposed this independent recovery bug.

---

# Impact

This issue also prevents adding a regression test for the original fix.

For example, a test such as:

```text
// RUN: not clang-repl "namespace v1 { int x = }"
```

never completes because the parser does not exit the recovery loop.

As a result:

- `clang-repl` never returns,
- `llvm-lit` hangs indefinitely,
- and the test suite stalls.

---

# Environment

- LLVM: current `main`
- Component: `clang-repl`
- Platform: Ubuntu (reproduced locally)
- Build: Debug
```

Contributor guide

Open the contributing guide

Research direction

Reproduce the hang with clang-repl using the namespace or brace-scope malformed statement shown in the issue, then inspect the parser recovery path named in the call stack: ParseExprStatement, ParseCompoundStatementBody, and ParseStatementOrDeclaration. Trace whether recovery advances past the closing brace and identify a way to restore forward progress; done means diagnostics are emitted and clang-repl returns normally in both interactive and non-interactive modes.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
cli, compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.