| Issue |
203891
|
| Summary |
Clang crashes on out-of-line template member function definition in a wrong namespace
|
| Labels |
|
| Assignees |
|
| Reporter |
Fedr
|
# Clang parser crash on out-of-line template member function definition in a wrong namespace
Compiler Explorer: https://gcc.godbolt.org/z/neT94c34f
## Reproducer (`mre.cpp`, 2 lines, 131 bytes)
```c++
struct C { template<class T> void f(); };
namespace N { template<class T> void C::f() { struct S { T* p; ~S() { delete p; } }; } }
```
## Command
```sh
clang++ -std=c++20 -fsyntax-only mre.cpp
```
Reproduces equivalently under `-c`, `-O0`/`-O2`, and on at least `-std=c++17/20/23`.
## Behavior
Clang emits the expected diagnostic for the invalid out-of-line definition…
```
mre.cpp:2:41: error: cannot define or redeclare 'f' here because namespace 'N' does not enclose namespace 'C'
2 | namespace N { template<class T> void C::f() { struct S { T* p; ~S() { delete p; } }; } }
| ~~~^
```
…and then **crashes** while continuing to parse the body of the local struct's destructor:
```
PLEASE submit a bug report ...
Stack dump:
1. mre.cpp:2:79: current parser token ';'
2. mre.cpp:2:1: parsing namespace 'N'
3. mre.cpp:2:45: parsing function body
4. mre.cpp:2:45: in compound statement ('{}')
5. mre.cpp:2:47: parsing struct/union/class body 'N::S'
6. mre.cpp:2:69: parsing function body 'N::S::~S'
7. mre.cpp:2:69: in compound statement ('{}')
```
## Root cause
The backtrace from `clang_trunk` (Linux, libc++) on Compiler Explorer is flat and non-recursive — a **null-pointer dereference deep in Sema**, not a stack overflow:
```
#4 clang::ASTContext::getTypeInfoImpl(clang::Type const*) const <-- crash
#5 clang::ASTContext::getTypeInfo(clang::Type const*) const
#6 clang::ASTContext::getTypeAlignIfKnown(clang::QualType, bool) const
#7 hasNewExtendedAlignment(clang::Sema&, clang::QualType)
#8 clang::Sema::ActOnCXXDelete(...)
#9 clang::Parser::ParseCXXDeleteExpression(...)
...
#19 clang::Parser::ParseLexedMethodDef ; late-parsed body of C::f<T>
#20 clang::Parser::ParseLexedMethodDefs ; fired at end of class S
#21 clang::Parser::ParseCXXMemberSpecification ; finishing local struct S
```
The assertion-enabled build (`clang50assert` on Godbolt) pinpoints the invariant being violated:
```
Assertion `!T->isDependentType() && "should not see dependent types here"' failed.
```
The body of `C::f<T>` is buffered as a late-parsed template method. When the local class `S` finishes, clang flushes the deferred bodies (`ParseLexedMethodDefs`) — including `~S()`. The destructor's `delete p` reaches `Sema::ActOnCXXDelete`, which asks for the alignment of `p`'s type via `getTypeAlignIfKnown` → `getTypeInfoImpl` — a code path documented to be called only on resolved (non-dependent) types. But because the namespace-mismatch error recovery has flushed `~S()` outside its template-instantiation context, the dependent template parameter `T` reaches type-info / alignment machinery that asserts it shouldn't see dependent types; in release builds the `Type*` is null and the dereference faults.
## Required ingredients
All of the following are required; removing any one suppresses the crash:
| ingredient | required? |
|--------------------------------------------------------|-----------|
| class `C` declared somewhere with a **template** member `f` | yes |
| out-of-line definition of `C::f` inside a namespace that does **not** enclose `C` | yes |
| local class/struct/union `S` inside the function body | yes |
| `S` has a data member of type **`T*`** (the template parameter) | yes |
| `S::~S()` (destructor) with a non-empty body that contains **`delete p`** (or `delete[] p`) | yes |
Removing the namespace mismatch (placing the definition where `C` is visible), using `int*` instead of `T*`, replacing `delete p` with `p = 0`, removing the template parameter, or using a non-destructor member function all make the crash disappear.
## Bisection on Compiler Explorer
Reproduced on clang 5.0.0, 5.0.1, 5.0.2, 6.0.0, 6.0.1, 7.0.0, 7.0.1, 7.1.0, 8.0.0, 8.0.1, 9.0.0, 9.0.1, 10.0.0, 10.0.1, 11.0.0, 12.0.0, 13.0.0, 14.0.0, 15.0.0, 19.1.0, 20.1.0, 21.1.0, and trunk.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs