Author: Helmut Januschka Date: 2026-08-13T15:48:13-07:00 New Revision: 517b22f7f0258e1b3d8ce5ba3ee34066cb64d529
URL: https://github.com/llvm/llvm-project/commit/517b22f7f0258e1b3d8ce5ba3ee34066cb64d529 DIFF: https://github.com/llvm/llvm-project/commit/517b22f7f0258e1b3d8ce5ba3ee34066cb64d529.diff LOG: [clang] Fix crash on implicit conversion of a reference NTTP bound to a subobject (#215900) A non-type template parameter of pointer or reference type is substituted with a ConstantExpr wrapping an OpaqueValueExpr that has no source expression, see BuildExpressionFromNonTypeTemplateArgumentValue(). When such a parameter appears in a context that needs an implicit conversion, the -Wconversion machinery inspects the expression, and TryGetExprRange() recursed into OpaqueValueExpr::getSourceExpr() without checking for null. The recursive call then dereferenced null in Expr::IgnoreParens(): struct S { static bool arr[2]; }; bool S::arr[2]; template <bool &Ref> int f() { return Ref; } int g() { return f<S::arr[1]>(); } Only recurse when there is a source expression, matching what CheckImplicitConversion() already does for OpaqueValueExpr, and otherwise fall back to the range of the expression's type. Added: Modified: clang/docs/ReleaseNotes.md clang/lib/Sema/SemaChecking.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 4962f9f137b5a..9a19bb2f2d5c7 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -476,6 +476,10 @@ features cannot lower the translation-unit ABI level; - Fixed merging of lambdas across modules in the case where neither lambda is imported from an AST file. (#GH214560) +- Fixed a crash when a non-type template parameter of reference type is bound + to a subobject and is used in a context that requires an implicit conversion. + (#GH215900) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f2f38c84dc5f8..3e6266b8ac542 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -12255,9 +12255,14 @@ static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E, } } - if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E)) - return TryGetExprRange(C, OVE->getSourceExpr(), MaxWidth, InConstantContext, - Approximate); + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E)) { + // The source expression is null for the OpaqueValueExpr that stands in for + // a non-type template argument of pointer or reference type; fall back to + // the range of the type in that case. + if (const Expr *SourceExpr = OVE->getSourceExpr()) + return TryGetExprRange(C, SourceExpr, MaxWidth, InConstantContext, + Approximate); + } if (const auto *BitField = E->getSourceBitField()) return IntRange(BitField->getBitWidthValue(), diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp index 8450ff037e184..924e27a7155a7 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp @@ -386,3 +386,20 @@ void test() { g<X>(); } } + +namespace GH215900 { +// A non-type template parameter of reference type bound to a subobject is +// represented as a ConstantExpr wrapping a source-less OpaqueValueExpr; the +// implicit conversion checks used to crash when walking into it. +struct S { + static bool arr[2]; + bool b; +}; +bool S::arr[2]; +S s; + +template <bool &Ref> int f() { return Ref; } +template <bool &Ref> int g() { int n = 0; n += Ref; return n; } + +int test() { return f<S::arr[1]>() + g<S::arr[0]>() + f<s.b>(); } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
