Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
add953a
refactor(semantic): rewrite TemplateResolver without Sema/TreeTransform
16bit-ykiko Jul 29, 2026
4a66055
refactor(semantic): remove allocator rebind hole from resolver
16bit-ykiko Jul 29, 2026
62571b7
feat(semantic): pseudo-SFINAE probe and template template substitution
16bit-ykiko Jul 29, 2026
6d2f24b
test(semantic): expand resolver suite, close TT-arg and value gaps
16bit-ykiko Jul 29, 2026
8b8743f
feat(semantic): pack element matching, expr handlers, call arity filter
16bit-ykiko Jul 29, 2026
30f461d
fix(semantic): keep probe verdict Unknown on guarded empty lookup
16bit-ykiko Jul 29, 2026
3f004e4
fix(semantic): compute aliased type for alias template heads
16bit-ykiko Jul 29, 2026
1419d0c
fix(semantic): compound deduction, outer param pinning, pack rewrite
16bit-ykiko Jul 29, 2026
1b91b38
fix(semantic): noexcept match, underlying_type, dependent absence
16bit-ykiko Jul 30, 2026
40de620
fix(semantic): array, method-qual, value-pack deduction; UTT guard
16bit-ykiko Jul 30, 2026
7eff0a2
fix(semantic): array bounds, pack element checks, malformed guards
16bit-ykiko Jul 30, 2026
42c5585
feat(semantic): splice value and function parameter packs
16bit-ykiko Jul 30, 2026
aa7a5c3
fix(semantic): pack cache bypass, inherited defaults, void ref guard
16bit-ykiko Jul 30, 2026
87cabca
test(semantic): crash-safety sweeps over libstdc++ and broken code
16bit-ykiko Jul 30, 2026
555a2ce
fix(semantic): dependent template heads, arity wiring, array guards
16bit-ykiko Jul 30, 2026
c0df033
fix(semantic): explicit object arity, param decay, value defaults
16bit-ykiko Jul 30, 2026
d3944bf
fix(semantic): head defaults, value packs, auto identity, cache guards
16bit-ykiko Jul 30, 2026
1acef1c
fix(semantic): TT defaults, non-trailing packs, return type guard
16bit-ykiko Jul 30, 2026
11abbb9
fix(semantic): array size forwarding, pack equality, surplus args
16bit-ykiko Jul 30, 2026
5a58e70
fix(semantic): noexcept deduction, TT arity, CC match, void guards
16bit-ykiko Jul 30, 2026
5dab810
fix(semantic): substitute bare noexcept operands at spec level
16bit-ykiko Jul 30, 2026
644b3f9
test(integration): wait for worker log before crash injection
16bit-ykiko Jul 30, 2026
27a2eca
fix(semantic): probe type check, cv on refs, auto bounds, TT kinds
16bit-ykiko Jul 30, 2026
c40736a
fix(semantic): suffix-aware packs, expansion checks, attributed types
16bit-ykiko Jul 30, 2026
0045ed8
fix(semantic): ambiguity degrade, dependent name args, TT pack splice
16bit-ykiko Jul 30, 2026
89c3582
fix(semantic): ext info, bounded arrays, empty pack cardinality
16bit-ykiko Jul 30, 2026
448814c
fix(semantic): value type agreement, access-aware probes
16bit-ykiko Jul 30, 2026
bc9028b
chore(semantic): mark NTTP expression gaps, pin no-crash degrade
16bit-ykiko Jul 30, 2026
af8a0e5
fix(semantic): stable pack slots, decayed params
16bit-ykiko Jul 30, 2026
59dd68f
fix(semantic): pack slot kinds, atomic, dependent bases, neg bounds
16bit-ykiko Jul 30, 2026
acae6f3
fix(semantic): pack prefix arity, template probes, arrow chains
16bit-ykiko Jul 30, 2026
16be8b8
fix(semantic): pack-arg calls, atomic guards, constraints, bounds
16bit-ykiko Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compile/compilation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ CompilationStatus CompilationUnitRef::Self::run_clang(

self.run_tidy();

if(instance.hasSema()) {
self.resolver.emplace(instance.getSema());
if(instance.hasASTContext()) {
self.resolver.emplace(instance.getASTContext());
}

return CompilationStatus::Completed;
Expand Down
2 changes: 1 addition & 1 deletion src/feature/hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,7 @@ auto decls_at(CompilationUnitRef unit, llvm::ArrayRef<clang::syntax::Token> touc
continue;
}

for(auto& occurrence: resolve_occurrences(sem_node, &unit.resolver())) {
for(auto& occurrence: resolve_occurrences(semantics, n, &unit.resolver())) {
auto location = occurrence.location;
if(location.isMacroID()) {
location = unit.spelling_location(location);
Expand Down
7 changes: 5 additions & 2 deletions src/feature/semantic_tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ class SemanticTokensCollector {
combine(token_semantics[index], candidate);
};

for(const auto& entry: semantics.node_entries()) {
for(auto [entry_index, entry]: llvm::enumerate(semantics.node_entries())) {
const SemanticNode& node = entry.node;
switch(node.kind()) {
case SemanticNode::Kind::MacroDefine: {
Expand Down Expand Up @@ -486,7 +486,10 @@ class SemanticTokensCollector {
}

default: {
for(auto& occurrence: resolve_occurrences(node, &unit.resolver())) {
for(auto& occurrence:
resolve_occurrences(semantics,
static_cast<std::uint32_t>(entry_index),
&unit.resolver())) {
anchor(occurrence.location,
classify_decl(occurrence.decl, occurrence.kind),
false);
Expand Down
2 changes: 1 addition & 1 deletion src/index/tu_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ class Projector {
continue;
}

for(auto& occurrence: resolve_occurrences(node, &unit.resolver())) {
for(auto& occurrence: resolve_occurrences(semantics, i, &unit.resolver())) {
add_occurrence(occurrence.decl, occurrence.kind, occurrence.location);

/// Every occurrence is mirrored as a self-relation, so
Expand Down
2,458 changes: 1,762 additions & 696 deletions src/semantic/resolver.cpp

Large diffs are not rendered by default.

60 changes: 18 additions & 42 deletions src/semantic/resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"

namespace clang {

class Sema;

}

namespace clice {

/// This class is used to resolve dependent names in the unit.
Expand All @@ -18,26 +12,18 @@ namespace clice {
/// some heuristics to simplify the dependent names as normal type/expression.
/// For example, `std::vector<T>::value_type` can be simplified as `T`.
///
/// Resolution is pure AST computation: it never enters Sema, so speculative
/// lookups cannot emit diagnostics, register specializations, or otherwise
/// mutate the unit's semantic state.
///
/// Thread safety: NOT thread-safe. Each compilation unit should have its own resolver.
/// The `resolved` cache persists across multiple resolve() calls on the same unit.
class TemplateResolver {
public:
explicit TemplateResolver(clang::Sema& sema) : sema(sema) {}
explicit TemplateResolver(clang::ASTContext& context) : context(context) {}

clang::QualType resolve(clang::QualType type);

void resolve(clang::CXXUnresolvedConstructExpr* expr);

void resolve(clang::UnresolvedLookupExpr* expr);

// TODO: Use a clearer approach for resolving UnresolvedLookupExpr.

void resolve(clang::UnresolvedUsingType* type);

/// Resugar the canonical `TemplateTypeParmType` with given template context.
/// `decl` should be the declaration that the type is in.
clang::QualType resugar(clang::QualType type, clang::Decl* decl);

using lookup_result = clang::DeclContext::lookup_result;

/// Look up the name in the given nested name specifier.
Expand All @@ -47,35 +33,25 @@ class TemplateResolver {
return lookup(type->getQualifier(), type->getIdentifier());
}

lookup_result lookup(const clang::DependentTemplateSpecializationType* type) {
auto& template_name = type->getDependentTemplateName();
auto identifier = template_name.getName().getIdentifier();
if(identifier) {
return lookup(template_name.getQualifier(), identifier);
} else {
/// TODO: Operators don't have an IdentifierInfo; need DeclarationName-based lookup.
return {};
}
}
lookup_result lookup(const clang::DependentTemplateSpecializationType* type);

lookup_result lookup(const clang::DependentScopeDeclRefExpr* expr) {
return lookup(expr->getQualifier(), expr->getNameInfo().getName());
}

lookup_result lookup(const clang::UnresolvedLookupExpr* expr) {
/// TODO: Only returns the first TemplateDecl; should handle overloaded lookups.
for(auto decl: expr->decls()) {
if(auto TD = llvm::dyn_cast<clang::TemplateDecl>(decl)) {
return lookup_result(TD);
}
}
lookup_result lookup(const clang::UnresolvedLookupExpr* expr);

return {};
}
/// Resolve the base type through pseudo-instantiation, then look the
/// member up in the resolved record. Complements the candidates clang
/// already stores on the expression: pseudo-instantiation can reach
/// members of matching partial specializations.
lookup_result lookup(const clang::UnresolvedMemberExpr* expr);

lookup_result lookup(const clang::UnresolvedMemberExpr* expr) {
return {};
}
/// Resolve a dependent call's candidate set, filtered down to overloads
/// whose parameter list can accept the call's argument count. Full
/// overload resolution needs conversion rules (Sema territory); arity is
/// the safe, conversion-free subset of it.
llvm::SmallVector<clang::NamedDecl*, 4> lookup(const clang::CallExpr* expr);

/// Resolve the base type through pseudo-instantiation, then look the
/// member up in the resolved record (e.g. `this->foo()` inherited from
Expand All @@ -91,7 +67,7 @@ class TemplateResolver {
}

private:
clang::Sema& sema;
clang::ASTContext& context;

/// Cache of resolved dependent types, keyed by AST node pointer.
/// Shared across resolve() calls within the same TU for performance.
Expand Down
71 changes: 65 additions & 6 deletions src/semantic/semantics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1334,7 +1334,10 @@ clang::SourceLocation keyword_after_scope(const clang::Decl* decl,
return token ? token->getLocation() : begin;
}

void stmt_occurrences(const clang::Stmt* S, Occurrences& out, TemplateResolver* resolver) {
void stmt_occurrences(const clang::Stmt* S,
Occurrences& out,
TemplateResolver* resolver,
const clang::CallExpr* call = nullptr) {
/// foo = 1
/// ^~~~ reference
if(auto* DRE = llvm::dyn_cast<clang::DeclRefExpr>(S)) {
Expand Down Expand Up @@ -1444,8 +1447,14 @@ void stmt_occurrences(const clang::Stmt* S, Occurrences& out, TemplateResolver*
/// ^~~~ weak reference
if(auto* DSDRE = llvm::dyn_cast<clang::DependentScopeDeclRefExpr>(S)) {
if(resolver) {
for(auto* target: resolver->lookup(DSDRE)) {
occur(out, target, RelationKind::WeakReference, DSDRE->getNameInfo().getLoc());
if(call) {
for(auto* target: resolver->lookup(call)) {
occur(out, target, RelationKind::WeakReference, DSDRE->getNameInfo().getLoc());
}
} else {
for(auto* target: resolver->lookup(DSDRE)) {
occur(out, target, RelationKind::WeakReference, DSDRE->getNameInfo().getLoc());
}
}
}
return;
Expand All @@ -1454,11 +1463,20 @@ void stmt_occurrences(const clang::Stmt* S, Occurrences& out, TemplateResolver*
/// foo(...) / obj.foo(...) where foo names an overload set — clang
/// already stores the candidates.
if(auto* OE = llvm::dyn_cast<clang::OverloadExpr>(S)) {
/// As a callee, the candidate set shrinks to the overloads that can
/// accept the call's argument count.
llvm::SmallVector<clang::NamedDecl*, 4> candidates;
if(call && resolver) {
candidates = resolver->lookup(call);
} else {
candidates.append(OE->decls_begin(), OE->decls_end());
}

/// Unwrap using shadows to the underlying functions, then reference
/// each introducing using-declaration once: hover picks it when the
/// overload set is otherwise ambiguous.
llvm::SmallPtrSet<const clang::UsingDecl*, 2> introducers;
for(auto* target: OE->decls()) {
for(auto* target: candidates) {
if(auto* shadow = llvm::dyn_cast<clang::UsingShadowDecl>(target)) {
if(auto* UD = llvm::dyn_cast<clang::UsingDecl>(shadow->getIntroducer())) {
introducers.insert(UD);
Expand Down Expand Up @@ -1494,8 +1512,14 @@ void stmt_occurrences(const clang::Stmt* S, Occurrences& out, TemplateResolver*
/// ^~~~ weak reference, resolved through the template resolver
if(auto* DSME = llvm::dyn_cast<clang::CXXDependentScopeMemberExpr>(S)) {
if(resolver) {
for(auto* target: resolver->lookup(DSME)) {
occur(out, target, RelationKind::WeakReference, DSME->getMemberLoc());
if(call) {
for(auto* target: resolver->lookup(call)) {
occur(out, target, RelationKind::WeakReference, DSME->getMemberLoc());
}
} else {
for(auto* target: resolver->lookup(DSME)) {
occur(out, target, RelationKind::WeakReference, DSME->getMemberLoc());
}
}
}
return;
Expand All @@ -1504,6 +1528,41 @@ void stmt_occurrences(const clang::Stmt* S, Occurrences& out, TemplateResolver*

} // namespace

llvm::SmallVector<NameOccurrence, 2> resolve_occurrences(const Semantics& semantics,
std::uint32_t index,
TemplateResolver* resolver) {
auto& entry = semantics.node(index);

/// A dependent name used as a callee resolves against the call, so the
/// candidate set can be filtered by arity. Walk up through the wrapper
/// nodes the tree keeps for parent chains.
if(auto* S = entry.node.get<clang::Stmt>();
S && llvm::isa<clang::OverloadExpr,
clang::DependentScopeDeclRefExpr,
clang::CXXDependentScopeMemberExpr>(S)) {
for(auto parent = entry.parent; parent != Semantics::invalid;
parent = semantics.node(parent).parent) {
auto* PS = semantics.node(parent).node.get<clang::Stmt>();
if(!PS) {
break;
}
if(auto* CE = llvm::dyn_cast<clang::CallExpr>(PS)) {
if(CE->getCallee()->IgnoreParenImpCasts() == S) {
llvm::SmallVector<NameOccurrence, 2> out;
stmt_occurrences(S, out, resolver, CE);
return out;
}
break;
}
if(!llvm::isa<clang::ParenExpr, clang::ImplicitCastExpr>(PS)) {
break;
}
}
}

return resolve_occurrences(entry.node, resolver);
}

llvm::SmallVector<NameOccurrence, 2> resolve_occurrences(const SemanticNode& node,
TemplateResolver* resolver) {
llvm::SmallVector<NameOccurrence, 2> out;
Expand Down
7 changes: 7 additions & 0 deletions src/semantic/semantics.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,11 @@ class Semantics {
std::vector<std::uint32_t> owner_nodes;
};

/// resolve_occurrences with tree context: a dependent name that is the
/// callee of a call (found through the node's parent chain) has its
/// candidate set filtered by the call's arity.
llvm::SmallVector<NameOccurrence, 2> resolve_occurrences(const Semantics& semantics,
std::uint32_t index,
TemplateResolver* resolver = nullptr);

} // namespace clice
Loading
Loading