summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorGangzheng Tong <tonggangzheng@gmail.com>2025-09-16 12:51:43 -0700
committerGitHub <noreply@github.com>2025-09-16 19:51:43 +0000
commitafb8146e10626887e3eb9f479480d4f8a1ad6128 (patch)
tree4a11668df1005f4c15e7e25d27615e3116961356 /source
parent8ad0ae17880480abe587617c997ab28b23abc146 (diff)
Diagnose error when the function args can't satisfy constexpr parameter requirements (#7269)
## Summary This PR enhances constexpr validation by adding proper error checking when function arguments cannot satisfy constexpr parameter requirements, addressing issue #6370. ## Problem Previously, when a function declared constexpr parameters, the compiler would attempt to propagate constexpr-ness to the call site arguments, but there was insufficient validation and error reporting when this propagation failed. This could lead silent failures where constexpr requirements weren't properly enforced ## Solution This PR adds checks that: 1. **Validates constexpr arguments**: When a function parameter is marked as `constexpr`, the compiler now explicitly checks that the corresponding argument can be marked as `constexpr` 2. **Issues clear compilation errors**: added `Diagnostics::argIsNotConstexpr`) 3. **Handles both call scenarios**: The validation works for both: - Direct function calls with IR-level function definitions - Calls to function from external modules Fixes #6370 --------- Co-authored-by: slangbot <ellieh+slangbot@nvidia.com> Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source')
-rw-r--r--source/slang/glsl.meta.slang9
-rw-r--r--source/slang/hlsl.meta.slang16
-rw-r--r--source/slang/slang-diagnostic-defs.h6
-rw-r--r--source/slang/slang-ir-constexpr.cpp92
4 files changed, 49 insertions, 74 deletions
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang
index 9112c1d2a..f63daf6b0 100644
--- a/source/slang/glsl.meta.slang
+++ b/source/slang/glsl.meta.slang
@@ -1914,7 +1914,7 @@ public vector<T.Element,4> texture(Sampler1D<T> sampler, float p)
__generic<T:ITexelElement>
[ForceInline]
[require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)]
-public vector<T.Element,4> texture(Sampler1D<T> sampler, float p, constexpr float bias)
+public vector<T.Element,4> texture(Sampler1D<T> sampler, float p, float bias)
{
return __vectorReshape2<T.Element, 4>(sampler.SampleBias(p, bias));
}
@@ -1950,7 +1950,7 @@ public vector<T.Element,4> texture(_Texture<
0, // isShadow
1, // isCombined
format
- > sampler, vector<float,Shape.dimensions+isArray> p, constexpr float bias)
+ > sampler, vector<float,Shape.dimensions+isArray> p, float bias)
{
return __vectorReshape2<T.Element,4>(sampler.SampleBias(p, bias));
}
@@ -3891,7 +3891,7 @@ public vector<T.Element,4> textureGatherOffset(_Texture<
0, // isShadow
1, // isCombined
format
- > sampler, vector<float,2+isArray> p, constexpr vector<int,2> offset, int comp = 0)
+ > sampler, vector<float,2+isArray> p, vector<int,2> offset, int comp = 0)
{
switch (comp)
{
@@ -3915,11 +3915,12 @@ public vec4 textureGatherOffset(_Texture<
1, // isShadow
1, // isCombined
format
- > sampler, vector<float,2+isArray> p, float refZ, constexpr vector<int,2> offset)
+ > sampler, vector<float,2+isArray> p, float refZ, vector<int,2> offset)
{
return sampler.GatherCmp(p, refZ, offset);
}
+
// -------------------
// textureGatherOffsets
// -------------------
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 6a1513f9c..fdec4e901 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -1064,7 +1064,7 @@ extension _Texture<T,Shape,isArray,isMS,sampleCount,0,isShadow,1,format>
[ForceInline]
__glsl_extension(GL_ARB_sparse_texture_clamp)
[require(cpp_glsl_hlsl_metal_spirv, texture_sm_4_1_clamp_fragment)]
- T Sample(vector<float, Shape.dimensions+isArray> location, vector<int, Shape.planeDimensions> offset, float clamp)
+ T Sample(vector<float, Shape.dimensions+isArray> location, constexpr vector<int, Shape.planeDimensions> offset, float clamp)
{
__requireComputeDerivative();
__target_switch
@@ -1091,7 +1091,7 @@ extension _Texture<T,Shape,isArray,isMS,sampleCount,0,isShadow,1,format>
[__readNone]
[ForceInline]
- T Sample(vector<float, Shape.dimensions+isArray> location, vector<int, Shape.planeDimensions> offset, float clamp, out uint status)
+ T Sample(vector<float, Shape.dimensions+isArray> location, constexpr vector<int, Shape.planeDimensions> offset, float clamp, out uint status)
{
__requireComputeDerivative();
__target_switch
@@ -3135,7 +3135,7 @@ vector<T.Element,4> __texture_gather_offset(
_Texture<T, Shape, isArray, 0, sampleCount, access, isShadow, 0, format> texture,
SamplerState s,
vector<float, Shape.dimensions+isArray> location,
- constexpr vector<int, Shape.planeDimensions> offset,
+ vector<int, Shape.planeDimensions> offset,
int component)
{
__target_switch
@@ -3196,7 +3196,7 @@ __generic<T:ITexelElement, Shape: __ITextureShape, let isArray:int, let sampleCo
vector<T.Element,4> __texture_gather_offset(
_Texture<T, Shape, isArray, 0, sampleCount, access, isShadow, 1, format> sampler,
vector<float, Shape.dimensions+isArray> location,
- constexpr vector<int, Shape.planeDimensions> offset,
+ vector<int, Shape.planeDimensions> offset,
int component)
{
__target_switch
@@ -3340,7 +3340,7 @@ vector<T.Element,4> __texture_gatherCmp_offset(
SamplerComparisonState s,
vector<float, Shape.dimensions+isArray> location,
T.Element compareValue,
- constexpr vector<int, Shape.planeDimensions> offset)
+ vector<int, Shape.planeDimensions> offset)
{
__target_switch
{
@@ -3386,7 +3386,7 @@ vector<T.Element,4> __texture_gatherCmp_offset(
_Texture<T, Shape, isArray, 0, sampleCount, access, isShadow, 1, format> sampler,
vector<float, Shape.dimensions+isArray> location,
T.Element compareValue,
- constexpr vector<int, Shape.planeDimensions> offset)
+ vector<int, Shape.planeDimensions> offset)
{
__target_switch
{
@@ -3532,7 +3532,7 @@ ${{{{
$(samplerParam)
vector<float, Shape.dimensions+isArray> location
$(compareParam),
- constexpr vector<int, Shape.planeDimensions> offset)
+ vector<int, Shape.planeDimensions> offset)
{
__target_switch
{
@@ -3558,6 +3558,7 @@ ${{{{
}
}
+
[ForceInline]
[require(hlsl, texture_gather)]
vector<T.Element,4> Gather$(compareFunc)$(componentFunc)(
@@ -3628,6 +3629,7 @@ ${{{{
${{{{
} // for (isScalarTexture)
}}}}
+
// End of all Texture Gather
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 64b1423ac..1f2fdd693 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -2442,11 +2442,8 @@ DIAGNOSTIC(
"\"index\"] attribute to provide a binding location.")
DIAGNOSTIC(40006, Error, unimplementedSystemValueSemantic, "unknown system-value semantic '$0'")
-
DIAGNOSTIC(49999, Error, unknownSystemValueSemantic, "unknown system-value semantic '$0'")
-DIAGNOSTIC(40006, Error, needCompileTimeConstant, "expected a compile-time constant")
-
DIAGNOSTIC(40007, Internal, irValidationFailed, "IR validation failed: $0")
DIAGNOSTIC(
@@ -2469,6 +2466,9 @@ DIAGNOSTIC(
unconstrainedGenericParameterNotAllowedInDynamicFunction,
"unconstrained generic paramter '$0' is not allowed in a dynamic function.")
+DIAGNOSTIC(40012, Error, needCompileTimeConstant, "expected a compile-time constant")
+
+DIAGNOSTIC(40013, Error, argIsNotConstexpr, "arg $0 in '$1' is not a compile-time constant")
DIAGNOSTIC(
40020,
diff --git a/source/slang/slang-ir-constexpr.cpp b/source/slang/slang-ir-constexpr.cpp
index 0bdbe260a..825bc5c98 100644
--- a/source/slang/slang-ir-constexpr.cpp
+++ b/source/slang/slang-ir-constexpr.cpp
@@ -3,6 +3,7 @@
#include "slang-ir-dominators.h"
#include "slang-ir-insts.h"
+#include "slang-ir-util.h"
#include "slang-ir.h"
namespace Slang
@@ -59,8 +60,12 @@ bool isConstExpr(IRInst* value)
case kIROp_StructKey:
case kIROp_WitnessTable:
case kIROp_Generic:
+ case kIROp_GlobalConstant:
return true;
-
+ case kIROp_Param:
+ if (isGenericParam(value))
+ return true;
+ break;
default:
break;
}
@@ -156,8 +161,10 @@ bool opCanBeConstExprByForwardPass(IRInst* value)
{
// TODO: handle call inst here.
- if (value->getOp() == kIROp_Param)
+ if (value->getOp() == kIROp_Param || value->getOp() == kIROp_Specialize)
+ {
return false;
+ }
return opCanBeConstExpr(value->getOp());
}
@@ -393,12 +400,6 @@ bool propagateConstExprBackward(PropagateConstExprContext* context, IRGlobalValu
// the callee for this call statically, and if so try to propagate
// constexpr from the parameters back to the arguments.
auto callInst = (IRCall*)ii;
-
- UInt operandCount = callInst->getOperandCount();
-
- UInt firstCallArg = 1;
- UInt callArgCount = operandCount - firstCallArg;
-
auto callee = callInst->getOperand(0);
// If we are calling a generic operation, then
@@ -423,64 +424,35 @@ bool propagateConstExprBackward(PropagateConstExprContext* context, IRGlobalValu
}
auto calleeFunc = as<IRFunc>(callee);
- if (calleeFunc && isDefinition(calleeFunc))
+ auto calleeType = callee->getDataType();
+ if (auto caleeFuncType = as<IRFuncType>(calleeType))
{
- // We have an IR-level function definition we are calling,
- // and thus we can propagate `constexpr` information
- // through its `IRParam`s.
-
- auto calleeFuncType = calleeFunc->getDataType();
-
- UInt callParamCount = calleeFuncType->getParamCount();
- SLANG_RELEASE_ASSERT(callParamCount == callArgCount);
-
- // If the callee has a definition, then we can read `constexpr`
- // information off of the parameters of its first IR block.
- if (auto calleeFirstBlock = calleeFunc->getFirstBlock())
+ UInt operandCount = callInst->getOperandCount();
+ UInt firstCallArg = 1;
+ UInt callArgCount = operandCount - firstCallArg;
+ auto paramCount = caleeFuncType->getParamCount();
+ SLANG_RELEASE_ASSERT(paramCount == callArgCount);
+ for (UInt pp = 0; pp < paramCount; ++pp)
{
- UInt paramCounter = 0;
- for (auto pp = calleeFirstBlock->getFirstParam(); pp;
- pp = pp->getNextParam())
+ auto paramType = caleeFuncType->getParamType(pp);
+ if (isConstExpr(paramType))
{
- UInt paramIndex = paramCounter++;
-
- auto param = pp;
- auto arg = callInst->getOperand(firstCallArg + paramIndex);
-
- if (isConstExpr(param))
+ auto arg = callInst->getOperand(firstCallArg + pp);
+ if (maybeMarkConstExprBackwardPass(context, arg))
{
- if (maybeMarkConstExprBackwardPass(context, arg))
- {
- changedThisIteration = true;
- }
+ changedThisIteration = true;
}
- }
- }
- }
- else
- {
- // If we don't have a concrete callee function
- // definition, then we need to extract the
- // type of the callee instruction, and try to work
- // with that.
- //
- // Note that this does not allow us to propagate
- // `constexpr` information from the body of a callee
- // back to call sites.
- auto calleeType = callee->getDataType();
- if (auto caleeFuncType = as<IRFuncType>(calleeType))
- {
- auto paramCount = caleeFuncType->getParamCount();
- for (UInt pp = 0; pp < paramCount; ++pp)
- {
- auto paramType = caleeFuncType->getParamType(pp);
- auto arg = callInst->getOperand(firstCallArg + pp);
- if (isConstExpr(paramType))
+ // If arg is not constexpr after this, meaning it can't be
+ // marked constexpr for some reason, but the param requires
+ // that. This is not expected.
+ if (!isConstExpr(arg))
{
- if (maybeMarkConstExprBackwardPass(context, arg))
- {
- changedThisIteration = true;
- }
+ context->getSink()->diagnose(
+ callInst->sourceLoc,
+ Diagnostics::argIsNotConstexpr,
+ pp + 1,
+ calleeFunc);
+ return false;
}
}
}