Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.

Pass names of non-type entities through as template arg values - #219

Open
conrade-ctc wants to merge 2 commits into
compiler-research:masterfrom
conrade-ctc:upstream-nttp-named-args
Open

Pass names of non-type entities through as template arg values#219
conrade-ctc wants to merge 2 commits into
compiler-research:masterfrom
conrade-ctc:upstream-nttp-named-args

Conversation

@conrade-ctc

@conrade-ctc conrade-ctc commented Jul 28, 2026

Copy link
Copy Markdown

A string template argument naming a constexpr variable or enum constant resolves to the entity's type today: AppendTypesSlow's identifier path calls Cpp::GetType, whose ValueDecl branch returns the variable's declared type with no value. Sema then sees a type argument for a non-type parameter and rejects the instantiation ("template argument for non-type template parameter must be an expression"):

cppyy.cppdef("template <int N> class C {}; constexpr int IntArg = 42;")
cppyy.gbl.C["IntArg"]   # TypeError (worked on cling-based cppyy)

This PR checks Cpp::GetNamed first: when the identifier names a variable or enum constant, it emits TemplateArgInfo{entity type, strdup(GetQualifiedCompleteName(...))} so the instantiation refers to the entity instead of its type. The comma-split fallback gets the symmetric check, covering qualified spellings ("ns::Val") and multi-argument strings. It also passes class/alias template names through as template-template arguments (null type + qualified name), and fixes true/false and negative integer literals in the identifier path (both previously produced a type-only argument that Sema rejected).

Requires the companion PR compiler-research/CppInterOp#1074, which teaches InstantiateTemplate / BestOverloadFunctionMatch to resolve a non-numeric m_IntegralValue as a named constant entity (DeclRefExpr, converted by Sema like a written argument). Notes for the FIXME discussion at #137 (comment) : this narrows the string-parsing surface rather than growing it — names are resolved through lookup, not parsed.

Intentionally out of scope: arbitrary constant expressions in strings ("IntArg + 1", "&x") — only (possibly qualified) names of constant entities and templates. cppyy TemplateProxy objects as template-template arguments now fail with a clean TypeError (previously a segfault via a null-type IsEnumType call); making them work needs a CPyCppyy AddTypeName change — left as follow-up.

Tests: the conversion logic is unit-tested in the companion CppInterOp PR (ScopeReflection_InstantiateTemplateNamedNTTPArg); end-to-end coverage in our downstream suite (class templates, function-template explicit args, enum constants, qualified names, const char* array decay). Happy to add a test_templates.py case to compiler-research/cppyy alongside if preferred.

🤖 Done with the help of Claude Code (Fable 5, human in the loop)

A string template arg naming a constexpr variable or enum constant used
to resolve to the entity's type, so Sema rejected it for non-type
parameters ("must be an expression"). Pass the qualified name in
m_IntegralValue instead; CppInterOp turns it into a reference.

Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
@conrade-ctc

Copy link
Copy Markdown
Author

Companion to the compiler-research/CppInterOp#1074 update: AppendTypesSlow now passes class/alias template names through as template-template args (null type + qualified name) — both in the identifier fast path and in the comma-split fallback for qualified spellings ("ns::Tmpl"). Two adjacent string-argument holes fixed on the way: true/false returned early from the identifier path with no value (the bool special-case lived only in the slow per-chunk fallback), and is_integral rejected negative literals ("-3"), so both previously produced a type-only argument that Sema rejected.

Comment thread clingwrapper/src/clingwrapper.cxx Outdated
// true/false are identifier-shaped value literals.
if (name == "true" || name == "false") {
types.emplace_back(Cpp::GetType("bool").data,
strdup(name == "true" ? "1" : "0"));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a copy of these literals? They are in the data section of the binary so we should not copy them.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — dropped the strdup. "1"/"0" are static string literals and TemplateArgInfo only borrows m_IntegralValue, so there is nothing to own.

Class/alias template names become template-template args (carried by
name, null type) in both the identifier path and the qualified-name
fallback; true/false map to bool values in the identifier fast path;
is_integral accepts negative literals.

Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
@conrade-ctc
conrade-ctc force-pushed the upstream-nttp-named-args branch from acb803f to 120cf92 Compare July 29, 2026 13:45
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants