diff options
| author | Gangzheng Tong <tonggangzheng@gmail.com> | 2025-09-16 12:51:43 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-16 19:51:43 +0000 |
| commit | afb8146e10626887e3eb9f479480d4f8a1ad6128 (patch) | |
| tree | 4a11668df1005f4c15e7e25d27615e3116961356 /source | |
| parent | 8ad0ae17880480abe587617c997ab28b23abc146 (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.slang | 9 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 16 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-constexpr.cpp | 92 |
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; } } } |
