Accept named constant entities as non-type template arguments - #1074
Accept named constant entities as non-type template arguments#1074conrade-ctc wants to merge 6 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1074 +/- ##
==========================================
+ Coverage 87.74% 87.79% +0.05%
==========================================
Files 23 23
Lines 6429 6458 +29
==========================================
+ Hits 5641 5670 +29
Misses 788 788
🚀 New features to boost your workflow:
|
|
A follow-up report on the same stack showed template-template arguments were still broken: a string naming a class template raised "Cannot find Templated Arg" (a template has no QualType, so the old type-only path had nothing to carry). The new commit extends the named-entity path: a non-numeric value naming a class/alias template now becomes a Template-kind Probing the argument matrix around this also surfaced two hardening fixes, included in the commit:
New unit tests: |
|
clang-tidy review says "All clean, LGTM! 👍" |
7850faa to
981d406
Compare
| llvm::StringRef Value(Info.m_IntegralValue); | ||
| llvm::StringRef Digits = Value; | ||
| (void)(Digits.consume_front("-") || Digits.consume_front("+")); | ||
| if (!ArgTy.isNull() && !Digits.empty() && | ||
| Digits.find_first_not_of("0123456789") == llvm::StringRef::npos) { | ||
| auto Res = llvm::APSInt(Value); | ||
| Res = Res.extOrTrunc(S.getASTContext().getIntWidth(ArgTy)); | ||
| return TemplateArgument(S.getASTContext(), Res, ArgTy); | ||
| } |
There was a problem hiding this comment.
On a second thought -- can we check if clang doesn't already have such conversion logic -- iirc there was something like that because we need to convert literals in the language.
There was a problem hiding this comment.
It does, and we already rely on it: every argument built here goes through CheckTemplateArgumentList, which applies the converted-constant-expression rules (CheckTemplateArgument -> BuildConvertedConstantExpression). The helper only builds the reference. It now uses Sema::BuildDeclarationNameExpr for that, so clang also picks the value kind and type - the enum-constant special case is gone. The numeric branch keeps the pre-existing extOrTrunc: the API accepts out-of-range literals that a strict conversion would reject, and Sema still re-checks the truncated value.
| return TemplateArgument(S.getASTContext(), Res, ArgTy); | ||
| } | ||
|
|
||
| Decl* Named = GetNamedFromCompleteName(Value.str()); |
There was a problem hiding this comment.
I believe we do not need to do parsing here. We should have the access to the declaration through ArgTy.
There was a problem hiding this comment.
ArgTy is the template parameter's type (int in the tests), so it cannot identify the entity - many declarations share one type, and a QualType never reaches a ValueDecl. For template-template arguments it is null by design. The name is the only handle the API carries, so I think the lookup should stay. The helper mirrors GetScopeFromCompleteName but also finds non-scope decls.
c8a4e3e to
01c9e38
Compare
A non-numeric TemplateArgInfo value now names a constexpr variable or enum constant: it is resolved and passed to Sema as a DeclRefExpr, so conversion (constant evaluation, array-to-pointer decay) matches a written argument. Shared by InstantiateTemplate and BestOverloadFunctionMatch, which previously fed the string to APSInt. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
A non-numeric value naming a class/alias template becomes a Template-kind argument (TemplateName). Fuzzing the arg matrix also found: IsEnumType crashed on a null type, and a TemplateArgInfo with null m_Type and no value reached TemplateArgument -- both now fail cleanly. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
BestOverloadFunctionMatch leaked Exprs on the failed-template-arg early return; own it with unique_ptr. Add the direct includes include-cleaner asked for: CppInterOpTypes.h (TemplateArgInfo) in the lib, Unwrap.h (HandleTypesTest precedent) and DeclTemplate.h in ScopeReflectionTest. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
The tidy bot flags unique_ptr<WrapperExpr[]> as a C-style array (cppcoreguidelines-avoid-c-arrays); a sized vector keeps the same placement-new pattern and early-return safety. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
Replace the manual DeclRefExpr construction with BuildDeclarationNameExpr. Clang selects the value kind and type as it does for written code. Sema still converts the argument in CheckTemplateArgumentList. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
Downstream code can query the named-argument support at run time. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
01c9e38 to
15fbfa4
Compare
Instantiating a template through cppyy's string subscript with the name of a constexpr variable or enum constant fails on the CppInterOp-based stack:
cling-based cppyy accepted this (the argument was pasted textually, so name lookup made it an expression). On this stack the name reaches
InstantiateTemplateas aTemplateArgInfowhosem_Typeis the entity's declared type and whosem_IntegralValueis a non-numeric string — which the current code feeds straight tollvm::APSInt(an assert/UB on non-digit input).This PR:
TemplateArgInfo→TemplateArgumentconversion shared byInstantiateTemplateandBestOverloadFunctionMatchinto one helper.m_IntegralValueon the existingAPSIntpath.m_IntegralValueas the (possibly qualified) name of a constant entity: it is resolved (aGetNamedwalk that, unlikeGetScopeFromCompleteName, also finds variables and enum constants) and wrapped in aDeclRefExpr, so Sema converts it exactly like a written argument — constant evaluation for integral parameters, array-to-pointer decay forconst char*parameters, prvalue handling for enum constants.m_IntegralValuenaming a class/alias template as a template-template argument (TemplateName).Companion PR: compiler-research/cppyy-backend#219 makes
AppendTypesSlowproduce these name-valuedTemplateArgInfos (today it resolves such names to their type viaCpp::GetType, which is how the bad argument arises). Related discussion on the string-argument surface: compiler-research/cppyy-backend#137 (comment)Tests: new
ScopeReflection_InstantiateTemplateNamedNTTPArgcovers a constexpr variable, a namespace-qualified name, an enum constant, char-array decay for aconst char*parameter, and unknown-name failure;ScopeReflection_InstantiateTemplateTemplateArgcovers plain/qualified/alias template-template arguments and null-info failure.check-cppinteropgreen.Out of scope (intentionally): arbitrary constant expressions in strings (
"IntArg + 1","&obj","'x'") — only (possibly qualified) names of constant entities, templates, and integer literals are resolved.🤖 Done with the help of Claude Code (Fable 5, human in the loop)
Adds
Cpp::SupportsNamedTemplateArguments()so callers can probe for this feature.