From afb8146e10626887e3eb9f479480d4f8a1ad6128 Mon Sep 17 00:00:00 2001 From: Gangzheng Tong Date: Tue, 16 Sep 2025 12:51:43 -0700 Subject: 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 Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> --- source/slang/slang-ir-constexpr.cpp | 92 +++++++++++++------------------------ 1 file changed, 32 insertions(+), 60 deletions(-) (limited to 'source/slang/slang-ir-constexpr.cpp') 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(callee); - if (calleeFunc && isDefinition(calleeFunc)) + auto calleeType = callee->getDataType(); + if (auto caleeFuncType = as(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(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; } } } -- cgit v1.2.3