(
options: Partial<CompileTarget<Expression>> = {}
)
| 3327 | `the shader builtin \`${callee}\` is declared over scalar and \`vecN\` ` + |
| 3328 | `genTypes; it has no ${hasMatrix ? '`matN`' : 'array'} overload, so ` + |
| 3329 | `the operand shapes (${shapes.map(gpuShapeName).join(', ')}) have no ` + |
| 3330 | `lowering.` |
| 3331 | ); |
| 3332 | declineWidths(); |
| 3333 | const reshapes = gpuReshapesOperands(code); |
| 3334 | if (reshapes !== undefined) |
| 3335 | decline( |
| 3336 | `the shader lowering \`${code}\` cannot take the non-scalar operand ` + |
| 3337 | `shapes (${shapes.map(gpuShapeName).join(', ')}) — ${reshapes}` |
| 3338 | ); |
| 3339 | // The shapes as they REACH the emitted call. A lowering is allowed to widen |
| 3340 | // a scalar operand itself, by writing out the broadcast constructor neither |
| 3341 | // language supplies (`Power` over a `vecN` base and a scalar exponent emits |
| 3342 | // `pow(v, vec3(y))`); the CE operand stays a scalar, but the argument that |
| 3343 | // stands in the call is a vector, so the mixed-genType checks below would |
| 3344 | // otherwise decline valid source. Only an UPGRADE is taken from the emitted |
| 3345 | // argument, and only where the lowering passes its operands through one for |
| 3346 | // one — a source that constructs no vector keeps its CE shape, so nothing |
| 3347 | // here can weaken a check. |
| 3348 | const emitted = shapes.map((s, i) => { |
| 3349 | if (s !== 'scalar' || argCount !== args.length) return s; |
| 3350 | return gpuConstructedVectorWidth(call.operands[i]) ?? s; |
| 3351 | }); |
| 3352 | if (widths.size === 1 && emitted.includes('scalar')) { |
| 3353 | const slots = rules.scalarGenTypeSlots.get(callee); |
| 3354 | if (slots === undefined) |
| 3355 | decline( |
| 3356 | `the shader builtin \`${callee}\` requires MATCHING genType ` + |
| 3357 | `arguments, but it is applied to a vec${[...widths][0]} operand and ` + |
| 3358 | `a scalar one; the scalar is not promoted to a vector.` |
| 3359 | ); |
| 3360 | // The overload says WHERE a scalar may stand, not merely that one may: |
| 3361 | // `mod(genType, float)` admits it only LAST, `step(float, genType)` only |
| 3362 | // FIRST, `mix(genType, genType, float)` only third. When the lowering |
| 3363 | // passes its operands through ONE FOR ONE the CE operand shapes give the |
| 3364 | // positions exactly — including for an operand with no constructor in its |
| 3365 | // source (a symbol declared `vector<3>`). |
| 3366 | if (argCount === args.length) { |
| 3367 | const bad = emitted.findIndex((s, i) => s === 'scalar' && !slots.has(i)); |
| 3368 | if (bad >= 0) |
| 3369 | decline( |
| 3370 | `the shader builtin \`${callee}\` takes a scalar only ` + |
| 3371 | `${gpuSlotNames(slots)}, but here the scalar stands in argument ` + |
| 3372 | `${bad + 1}, where the overload requires the vec${ |
| 3373 | [...widths][0] |
| 3374 | } genType; the scalar is not promoted to a vector.` |
| 3375 | ); |
| 3376 | } |
| 3377 | // A lowering that does NOT pass its operands through (the variadic |
| 3378 | // `min`/`max` fold) is judged on the emitted call TREE, where each nested |
| 3379 | // call has its own argument positions. |
| 3380 | const misplaced = gpuMisplacedScalarArgument( |
| 3381 | code, |
| 3382 | rules.scalarGenTypeSlots |
| 3383 | ); |
| 3384 | if (misplaced !== undefined) decline(misplaced); |
nothing calls this directly
no test coverage detected