From 8c0f085ee39533560dcd8624ad854fdb89effaee Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Wed, 28 Jan 2026 15:54:30 -0800 Subject: [PATCH 1/2] Fix crash in CanConvert from incomplete type during overload candidate selection A crash could be triggered in IsHLSLCopyableAnnotatableRecord when checking whether an explicit cast would be allowed for diagnostics, when adding function overload candidates with an incomplete target param type, and the source type is a basic type that supports splat to record type. Fixed by attempting to complete the type in CanConvert before calling IsHLSLNumericOrAggregateOfNumericType (which calls IsHLSLCopyableAnnotatableRecord). If it fails to complete, it will harmlessly skip this case. --- tools/clang/lib/Sema/SemaHLSL.cpp | 1 + .../incomplete-target-in-CanConvert.hlsl | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 2b3f065d71..5419863872 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -9901,6 +9901,7 @@ bool HLSLExternalSource::CanConvert(SourceLocation loc, Expr *sourceExpr, // We can only splat to target types that do not contain object/resource // types if (sourceSingleElementBuiltinType != nullptr && + !m_sema->RequireCompleteType(loc, target, 0) && hlsl::IsHLSLNumericOrAggregateOfNumericType(target)) { BuiltinType::Kind kind = sourceSingleElementBuiltinType->getKind(); switch (kind) { diff --git a/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl b/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl new file mode 100644 index 0000000000..282a96959a --- /dev/null +++ b/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl @@ -0,0 +1,29 @@ +// RUN: %dxc -T vs_6_0 -E main %s | FileCheck %s + +// CHECK: %[[IN:[^ ]+]] = call float @dx.op.loadInput.f32(i32 4, i32 0, i32 0, i8 0, i32 undef) +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float %[[IN]]) + +template struct Wrapper; +float get(float x) { return x;} +float get(Wrapper o); + +template +struct Wrapper { + T value; + void set(float x) { + // Incomplete target in CanConvert triggered during check whether explicit + // cast would be allowed for converting to overload candidate 'float + // get(Wrapper)'. CanConvert would check spat potential when + // explicit and source is a basic numeric type. This would cause crash in + // IsHLSLCopyableAnnotatableRecord from the incomplete type. + value = get(x); + } +}; + +float get(Wrapper o) { return o.value; } + +float main(float x : IN) : OUT { + Wrapper w; + w.set(x); + return get(w); +} From 8b0c2b8ab3e065986411c80f1be9e00b402c9431 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Thu, 29 Jan 2026 12:04:18 -0800 Subject: [PATCH 2/2] test: include type that can't be completed at call site --- .../templates/incomplete-target-in-CanConvert.hlsl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl b/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl index 282a96959a..bd559d2ed1 100644 --- a/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl +++ b/tools/clang/test/CodeGenDXIL/templates/incomplete-target-in-CanConvert.hlsl @@ -7,6 +7,9 @@ template struct Wrapper; float get(float x) { return x;} float get(Wrapper o); +template struct Wrapper2; +float get(Wrapper2 o); + template struct Wrapper { T value; @@ -16,14 +19,21 @@ struct Wrapper { // get(Wrapper)'. CanConvert would check spat potential when // explicit and source is a basic numeric type. This would cause crash in // IsHLSLCopyableAnnotatableRecord from the incomplete type. + // Here, we can complete the type: Wrapper, but not Wrapper2 value = get(x); } }; float get(Wrapper o) { return o.value; } +template +struct Wrapper2 : Wrapper { +}; + +float get(Wrapper2 o) { return o.value; } + float main(float x : IN) : OUT { - Wrapper w; + Wrapper2 w; w.set(x); return get(w); }