Files
clice/tests/unit/unit_tests.cc
ykiko a1b6c0632d refactor(semantic): rewrite template resolver — eliminate SubstType, fix crashes (#388)
## Summary

Rewrites the template resolver to eliminate
`SubstType`/`CodeSynthesisContexts` dependency, fixing widespread
crashes on real-world C++ code.

### What changed

**Architecture**: replaced double-TreeTransform (PseudoInstantiator +
SubstType) with single-layer design:
- **`SubstituteOnly`** — new lightweight TreeTransform for Phase 2
(typedef expansion + parameter substitution). Does NOT override
`TransformDependentNameType`, breaking the typedef ↔ lookup infinite
cycle.
- **`PseudoInstantiator`** — retains heuristic lookup (the unique value
clang doesn't provide), delegates substitution to `SubstituteOnly`.

**Deleted**:
- `DesugarOnly` class
- `instantiate()` method and all `CodeSynthesisContexts` / `SubstType`
usage
- `state()` / `rewind()` stack management
- `std::abort()` on valid NNS kinds
- `#ifndef NDEBUG` debug flag + `std::print` logging

**Added**:
- `SubstituteOnly` class with depth guard
- `InstantiationStack::findArgument()` — depth/index based parameter
lookup
- CTD→TST resolution for `DependentTemplateSpecializationType` (enables
`__alloc_traits::rebind<T>::other` resolution)
- `active_resolutions` (DNT cycle detection) + `active_ctd_lookups` (CTD
cycle detection via RAII guard)
- Stack frame pollution fix: pop lookup frames before further
`TransformType`
- Pack argument support (single-element forwarding)
- Null safety on all Transform return paths
- Structured `LOG_DEBUG` trace logging with indentation
- `--log-level` / `--test-filter` CLI options for unit test runner
- Bounds checks in `hole()`, `ResugarOnly`, `visitTemplateDeclContexts`

**Tests**: 20 → 36 passing test cases (+5 documented TODOs for known
limitations). New coverage: recursive base classes, multiple
inheritance, typedef chains, CRTP, `remove_reference` partial specs,
`std::map`, `std::basic_string`, pack forwarding.

### Stress test result

```
CDB: llvm-project build (4669 C++ files)
Types resolved: 3,690,190
Types unchanged: 75,907,298
Crashes: 0
```

Before this PR, the same test produced ~52% crash rate (413 crashes in
800 files).

### Known limitations (documented as TODOs)

- NTTP partial specialization matching (`enable_if<true, X>`, `A<X, 0>`)
- Template template parameter deduction
- Non-dependent qualifier nested class templates
(`Outer<int>::Inner<X>`)
- Multi-element pack expansion
- `CXXDependentScopeMemberExpr` lookup (unimplemented)
- Operator-name lookup in dependent contexts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Enhanced template resolution with improved cycle detection to prevent
infinite loops
  * Better type substitution handling for complex dependent types

* **Bug Fixes**
  * Fixed edge cases in template specialization resolution
  * Improved null-safety in type transformations
  * Enhanced handling of standard library template traits

* **Tests**
  * Expanded test coverage for recursive and complex template patterns
  * Added validation for standard library type resolution

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:18:07 +08:00

59 lines
2.0 KiB
C++

#include <string>
#include <string_view>
#include "eventide/deco/deco.h"
#include "eventide/zest/zest.h"
#include "support/logging.h"
namespace {
constexpr auto kv_style = static_cast<char>(deco::decl::KVStyle::Joined) |
static_cast<char>(deco::decl::KVStyle::Separate);
struct TestOptions {
DecoKVStyled(kv_style, names = {"--test-filter", "--test-filter="};
help = "Filter tests by name";
required = false;)
<std::string> test_filter;
DecoKVStyled(kv_style, names = {"--log-level", "--log-level="};
help = "Log level: trace/debug/info/warn/err";
required = false;)
<std::string> log_level;
DecoKVStyled(kv_style, names = {"--test-dir", "--test-dir="}; help = "Test data directory";
required = false;)
<std::string> test_dir;
};
} // namespace
int main(int argc, const char** argv) {
auto args = deco::util::argvify(argc, argv);
auto parsed = deco::cli::parse<TestOptions>(args);
std::string_view filter = {};
if(parsed.has_value() && parsed->options.test_filter.has_value()) {
filter = *parsed->options.test_filter;
}
if(parsed.has_value() && parsed->options.log_level.has_value()) {
auto level = *parsed->options.log_level;
if(level == "trace") {
clice::logging::options.level = clice::logging::Level::trace;
} else if(level == "debug") {
clice::logging::options.level = clice::logging::Level::debug;
} else if(level == "info") {
clice::logging::options.level = clice::logging::Level::info;
} else if(level == "warn") {
clice::logging::options.level = clice::logging::Level::warn;
} else if(level == "err") {
clice::logging::options.level = clice::logging::Level::err;
}
}
clice::logging::stderr_logger("test", clice::logging::options);
return eventide::zest::Runner::instance().run_tests(filter);
}