[clang] [clang] fix crash related to missing source locations for converted template arguments (PR #187352)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 19 09:32:32 PDT 2026
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/187352
>From 772472683a4c90376b491a7a3939ef79b8cd0bbf Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Tue, 17 Mar 2026 14:22:21 -0300
Subject: [PATCH] [clang] fix crash related to missing source locations for
converted template arguments
This adds a way to attach source locations to trivially created template
arguments such as packs, or converted expressions when there is no
expression anymore.
This also avoids crashes due to missing source locations.
In a few places where this matters, we already create expressions
from the converted arguments, but this requires access to Sema,
where currently creating trivial typelocs only requires access to
to the ASTContext.
So this creates a new storage kind for TemplateArgumentLocs, where
a single SourceLocation is stored, embedded in the pointer where
possible.
As a drive-by, strenghten asserts by enforcing the TemplateArgumentLocs
are created with the right kinds of locations.
Fixes #186655
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/AST/TemplateBase.h | 56 ++++++++++++++++++++++--
clang/lib/AST/TemplateBase.cpp | 19 ++++++++
clang/lib/AST/TypeLoc.cpp | 7 +--
clang/lib/Sema/SemaExpr.cpp | 8 ++--
clang/lib/Sema/SemaTemplate.cpp | 6 +--
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +-
clang/test/SemaCXX/type_pack_element.cpp | 7 +++
8 files changed, 90 insertions(+), 16 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e1adaba4be7fc..71e955cfd0b53 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -389,6 +389,7 @@ Miscellaneous Clang Crashes Fixed
- Fixed a crash when using loop hint with a value dependent argument inside a
generic lambda. (#GH172289)
- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
+- Fixed a crash related to missing source locations (#GH186655)
- Fixed a crash when casting a parenthesized unresolved template-id or array section. (#GH183505)
- Fixed a crash when initializing a ``constexpr`` pointer with a floating-point literal in C23. (#GH180313)
- Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319)
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index de248ac3cf703..c0db55ea92cd5 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -493,6 +493,10 @@ struct TemplateArgumentLocInfo {
TemplateArgumentLocInfo(TypeSourceInfo *Declarator) { Pointer = Declarator; }
TemplateArgumentLocInfo(Expr *E) { Pointer = E; }
+
+ // For trivial source locations for converted template argument kinds.
+ TemplateArgumentLocInfo(ASTContext &Ctx, SourceLocation Loc);
+
// Ctx is used for allocation -- this case is unusually large and also rare,
// so we store the payload out-of-line.
TemplateArgumentLocInfo(ASTContext &Ctx, SourceLocation TemplateKwLoc,
@@ -518,9 +522,28 @@ struct TemplateArgumentLocInfo {
return getTemplate()->EllipsisLoc;
}
+ bool isNull() const { return Pointer.isNull(); }
+
+ bool isTrivial() const { return isa<LocOrPointer>(Pointer); }
+
+ SourceLocation getTrivialLoc() const {
+ auto *P = cast<LocOrPointer>(Pointer);
+ if constexpr (EmbedLocInPointer)
+ return SourceLocation::getFromRawEncoding(
+ (reinterpret_cast<uintptr_t>(P) >> LowBitsRequired) - 1u);
+ else
+ return *static_cast<SourceLocation *>(P);
+ }
+
private:
- llvm::PointerUnion<TemplateTemplateArgLocInfo *, Expr *, TypeSourceInfo *>
+ static constexpr bool EmbedLocInPointer = sizeof(void *) >
+ sizeof(SourceLocation);
+ using LocOrPointer =
+ std::conditional_t<EmbedLocInPointer, void, SourceLocation> *;
+ llvm::PointerUnion<TemplateTemplateArgLocInfo *, Expr *, TypeSourceInfo *,
+ LocOrPointer>
Pointer;
+ static constexpr unsigned LowBitsRequired = 2;
};
/// Location wrapper for a TemplateArgument. TemplateArgument is to
@@ -534,16 +557,43 @@ class TemplateArgumentLoc {
TemplateArgumentLoc(const TemplateArgument &Argument,
TemplateArgumentLocInfo Opaque)
- : Argument(Argument), LocInfo(Opaque) {}
+ : Argument(Argument), LocInfo(Opaque) {
+ switch (Argument.getKind()) {
+ case TemplateArgument::Null:
+ assert(Opaque.isNull());
+ return;
+ case TemplateArgument::Pack:
+ assert(Opaque.isTrivial());
+ return;
+ case TemplateArgument::NullPtr:
+ case TemplateArgument::Integral:
+ case TemplateArgument::Declaration:
+ case TemplateArgument::StructuralValue:
+ assert(Opaque.isTrivial() || Opaque.getAsExpr() != nullptr);
+ return;
+ case TemplateArgument::Expression:
+ assert(Opaque.getAsExpr() != nullptr);
+ return;
+ case TemplateArgument::Type:
+ assert(Opaque.getAsTypeSourceInfo() != nullptr);
+ return;
+ case TemplateArgument::Template:
+ case TemplateArgument::TemplateExpansion:
+ assert(Opaque.getTemplate() != nullptr);
+ return;
+ }
+ llvm_unreachable("Unknown TemplateArgument kind");
+ }
TemplateArgumentLoc(const TemplateArgument &Argument, TypeSourceInfo *TInfo)
: Argument(Argument), LocInfo(TInfo) {
assert(Argument.getKind() == TemplateArgument::Type);
+ assert(TInfo != nullptr);
}
TemplateArgumentLoc(const TemplateArgument &Argument, Expr *E)
: Argument(Argument), LocInfo(E) {
-
+ assert(E != nullptr);
// Permit any kind of template argument that can be represented with an
// expression.
assert(Argument.getKind() == TemplateArgument::NullPtr ||
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 131ae6e8a478f..ee5bb80fc36ee 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -626,9 +626,13 @@ SourceRange TemplateArgumentLoc::getSourceRange() const {
return getSourceExpression()->getSourceRange();
case TemplateArgument::Declaration:
+ if (LocInfo.isTrivial())
+ return SourceRange(LocInfo.getTrivialLoc());
return getSourceDeclExpression()->getSourceRange();
case TemplateArgument::NullPtr:
+ if (LocInfo.isTrivial())
+ return SourceRange(LocInfo.getTrivialLoc());
return getSourceNullPtrExpression()->getSourceRange();
case TemplateArgument::Type:
@@ -650,12 +654,18 @@ SourceRange TemplateArgumentLoc::getSourceRange() const {
return SourceRange(getTemplateNameLoc(), getTemplateEllipsisLoc());
case TemplateArgument::Integral:
+ if (LocInfo.isTrivial())
+ return SourceRange(LocInfo.getTrivialLoc());
return getSourceIntegralExpression()->getSourceRange();
case TemplateArgument::StructuralValue:
+ if (LocInfo.isTrivial())
+ return SourceRange(LocInfo.getTrivialLoc());
return getSourceStructuralValueExpression()->getSourceRange();
case TemplateArgument::Pack:
+ return SourceRange(LocInfo.getTrivialLoc());
+
case TemplateArgument::Null:
return SourceRange();
}
@@ -737,6 +747,15 @@ clang::TemplateArgumentLocInfo::TemplateArgumentLocInfo(
Pointer = Template;
}
+clang::TemplateArgumentLocInfo::TemplateArgumentLocInfo(
+ ASTContext &Ctx, SourceLocation TrivialLoc) {
+ if constexpr (EmbedLocInPointer)
+ Pointer = reinterpret_cast<void *>((TrivialLoc.getRawEncoding() + 1u)
+ << LowBitsRequired);
+ else
+ Pointer = new (Ctx) SourceLocation(TrivialLoc);
+}
+
const ASTTemplateArgumentListInfo *
ASTTemplateArgumentListInfo::Create(const ASTContext &C,
const TemplateArgumentListInfo &List) {
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 53edfdb65a4d5..7e72a85136966 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -723,11 +723,12 @@ void TemplateSpecializationTypeLoc::initializeArgLocs(
case TemplateArgument::Null:
llvm_unreachable("Impossible TemplateArgument");
+ case TemplateArgument::Pack:
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::NullPtr:
case TemplateArgument::StructuralValue:
- ArgInfos[i] = TemplateArgumentLocInfo();
+ ArgInfos[i] = TemplateArgumentLocInfo(Context, Loc);
break;
case TemplateArgument::Expression:
@@ -755,10 +756,6 @@ void TemplateSpecializationTypeLoc::initializeArgLocs(
: Loc);
break;
}
-
- case TemplateArgument::Pack:
- ArgInfos[i] = TemplateArgumentLocInfo();
- break;
}
}
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index db6d93ce54791..2b0a786302aab 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2366,14 +2366,14 @@ Sema::ActOnStringLiteral(ArrayRef<Token> StringToks, Scope *UDLScope) {
TemplateArgumentLocInfo TypeArgInfo(Context.getTrivialTypeSourceInfo(CharTy));
ExplicitArgs.addArgument(TemplateArgumentLoc(TypeArg, TypeArgInfo));
+ SourceLocation Loc = StringTokLocs.back();
for (unsigned I = 0, N = Lit->getLength(); I != N; ++I) {
Value = Lit->getCodeUnit(I);
TemplateArgument Arg(Context, Value, CharTy);
- TemplateArgumentLocInfo ArgInfo;
+ TemplateArgumentLocInfo ArgInfo(Context, Loc.getLocWithOffset(I));
ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo));
}
- return BuildLiteralOperatorCall(R, OpNameInfo, {}, StringTokLocs.back(),
- &ExplicitArgs);
+ return BuildLiteralOperatorCall(R, OpNameInfo, {}, Loc, &ExplicitArgs);
}
case LOLR_Raw:
case LOLR_ErrorNoDiagnostic:
@@ -3915,7 +3915,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
for (unsigned I = 0, N = Literal.getUDSuffixOffset(); I != N; ++I) {
Value = TokSpelling[I];
TemplateArgument Arg(Context, Value, Context.CharTy);
- TemplateArgumentLocInfo ArgInfo;
+ TemplateArgumentLocInfo ArgInfo(Context, TokLoc.getLocWithOffset(I));
ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo));
}
return BuildLiteralOperatorCall(R, OpNameInfo, {}, TokLoc, &ExplicitArgs);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c3ff9f83be364..5d23660585ea5 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5972,9 +5972,9 @@ bool Sema::CheckTemplateArgumentList(
CTAI.CanonicalConverted,
Params->getDepth()));
}
- ArgLoc =
- TemplateArgumentLoc(TemplateArgument::CreatePackCopy(Context, Args),
- ArgLoc.getLocInfo());
+ ArgLoc = TemplateArgumentLoc(
+ TemplateArgument::CreatePackCopy(Context, Args),
+ TemplateArgumentLocInfo(Context, ArgLoc.getLocation()));
} else {
SaveAndRestore _1(CTAI.PartialOrdering, false);
if (CheckTemplateArgument(*Param, ArgLoc, Template, TemplateLoc,
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 265e5f4d2491d..f18a34415ba43 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2908,7 +2908,7 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
return TemplateArgumentLoc(Arg, Arg.getAsExpr());
case TemplateArgument::Pack:
- return TemplateArgumentLoc(Arg, TemplateArgumentLocInfo());
+ return TemplateArgumentLoc(Arg, TemplateArgumentLocInfo(Context, Loc));
}
llvm_unreachable("Invalid TemplateArgument Kind!");
diff --git a/clang/test/SemaCXX/type_pack_element.cpp b/clang/test/SemaCXX/type_pack_element.cpp
index d22d5fa2ba67c..0509358887af0 100644
--- a/clang/test/SemaCXX/type_pack_element.cpp
+++ b/clang/test/SemaCXX/type_pack_element.cpp
@@ -43,3 +43,10 @@ static_assert(__is_same(__type_pack_element<5, X<0>, X<1>, X<2>, X<3>, X<4>, X<5
template <SizeT Index, typename ...T>
using ErrorTypePackElement1 = __type_pack_element<Index, T...>; // expected-error{{may not be accessed at an out of bounds index}}
using illformed1 = ErrorTypePackElement1<3, X<0>, X<1>>; // expected-note{{in instantiation}}
+
+namespace GH186655 {
+ template <class {}, class C> struct S;
+ // expected-error at -1 {{cannot be defined in a type specifier}}
+ template <int T> struct S<T, __type_pack_element<sizeof(T)>> {};
+ // expected-error at -1 {{a parameter pack may not be accessed at an out of bounds index}}
+} // namespace GH186655
More information about the cfe-commits
mailing list