규칙 8.18
8장, 함수와 진입점 테스트
A function whose parameter has a function type, written out (f: (x: f32) => f32) or through a type alias of one, takes a function: a function declared at the top of the file or of a namespace, a method, a static method, a constructor, a field that holds a function (Rule 8.16) and a local function (Rule 8.17). It is compiled once for each set of functions its calls hand it, as a generic function is once for each set of type arguments (Rule 8.9), and in each copy a call of the parameter calls the function handed over (apply_sq, apply_run_f, Swarm_each_sq, run_twice_run_f).
A call hands a function over by its name (a module function, a local function, or a parameter of function type of the function the call is in) or as an arrow function or a function expression written in the call, which is a local function of the calling body (Rule 8.17) and takes its parameter types, and its return type where it writes none, from the parameter’s type.
What the function handed over captures, the copy takes as parameters too and passes on, by reference where the function writes it.
A variable the copy and a function handed over both reach is passed once, as TypeScript has it once: a variable a local function and the function it is handed both capture, and the object a method is called on when a function handed over captures that very variable (this, or a local named as the object), which the copy then takes as its object, by reference where the caller may write it (Rule 8.10).
A function handed over takes the parameter’s types and returns its type, or any type where the parameter’s returns void, whose value is not used; an arrow function may leave parameters off at the end, as TypeScript allows, and one whose body is an expression and whose type returns void runs the expression as a statement.
Inside the function that takes it, a parameter of function type may only be called or handed on to another such parameter.
A fold’s function (any(xs, (x) => x > k), zip(xs, ys, (a, b) => a * b)) is handed over the same way, its types read off the arrays, and an arrow function whose return type nothing gives returns what its body does.
An array’s method (xs.map(f), xs.forEach(f), xs.some(p), xs.every(p), xs.reduce(f, init), surface §63) takes a function the same way, a function the file declares taking as few of the arguments as it declares: each call is a call of a function of the module made once for each array type and function handed over (array_map_sq), a counted loop over the indices (Rule 7.5) that hands the function the element, its index as an i32 and the array, and for reduce the running value first, which has the type of the function’s first parameter where that is written and of the value to start from otherwise. The loop reads the array as it goes: a module variable, a module constant or a binding in place, the variable a function handed over captures through the one reference both use, and any other array by value, an index on the way to it read once.
Of an array’s methods, one Array.prototype has beyond those five, map on an array with no size, whose value would be an array with none, reduce with no value to start from on an array with no size, which may be empty, a second argument to any but reduce (thisArg), and a function a method passes an array with no size to must be refused.
A function type anywhere else (a return, a field, a variable), a parameter of function type on a setter or an entry point, an argument that would choose a function at run time (c ? f : g), a function whose copies would never end (one that hands itself a function it builds anew each time), and a call on a field or an element of a variable that a function handed over reaches, where one of the two may write it, which would be two references into one variable, must be refused.
규칙 본문과 그 아래 항목은 설계 문서에 적힌 컴파일러 원문이라 한국어 페이지에서도 영어로 둡니다.
근거
TypeScript code hands functions to functions all the time: a callback to a loop helper, a predicate to a search, an operator to a fold.
The GPU has no function value, but every call names the function it hands over, so each call reaches exactly one function, and a copy for each is what the program means; the copies are a generic function’s instances by another key, and the captures, the TDZ and the cycle check are Rule 8.17’s and Rule 8.4’s.
Before this rule a function type was “Unsupported type syntax” and an arrow function written as an argument “Unsupported expression”.
A copy of a method has an object, and a copy of a local function has captures of its own; each is one more parameter of the copy, and where one of them and a function handed over reach one variable, the copy takes one reference to it, since two references into one variable are refused by WGSL where either is written (Alias Analysis) and would let the method read a stale copy where they are not. A setter’s value is given by an assignment, which hands it no function.
An array’s method is a loop, and a method call is an expression, which may stand in an argument, beside && or in a loop’s condition, where no loop can be written in place; a function of the module is a call anywhere one may be, and every target and the CPU oracle run one already, so the IR gains no node. Unrolled, as the folds are, the code would grow with the array and would have no length to unroll to for a runtime-sized array, the one a compute kernel meets first. Before this rule every method of an array was refused.
출처
ECMAScript function objects (a function is a value a call hands over); ECMAScript Array.prototype.map and its four siblings, which read each element as the loop reaches it; TypeScript’s contextual typing of a function expression and its rule that a function with fewer parameters is assignable; WGSL’s Alias Analysis; Rule 8.4; Rule 8.9; Rule 8.10; Rule 8.16; Rule 8.17; surface §14, §26 and §63.
검증 방식
테스트로 확인합니다. 테스트나 게이트 스크립트, CI 워크플로가 이 규칙을 가리키고, 아래 파일 가운데 하나라도 규칙을 더는 가리키지 않으면 추적 검사가 실패합니다.
컴파일러가 이 규칙을 적용하는 곳을 규칙이 직접 적은 내용입니다.
TS8003 TYPE_MISMATCH for a function that does not fit ("add" takes 2 argument(s), and "(x: f32) => f32" passes 1, so it cannot be "f" of "apply".), TS8099 for a choice at run time ("f" of "apply" takes a function, which a call hands over by its name or as an arrow function written there; "c ? sq : cube" would choose one at run time, and a shader has no function value to choose with.), TS8020 FUNCTION_SHAPE for a parameter of function type where none may be ("f" takes a function, and "G.each" is a setter, whose value an assignment gives it: a shader has no function value to assign. Take the function in a method instead (Rule 8.18).), TS8002 for a function type anywhere else, TS8031 RECURSION for copies without end, and TS8099 for two references into one variable ("f" of "Inner.each" reaches "this", which holds "this.inner", the object the call is on, so the call would take two references into one variable, which WGSL refuses where either is written. Call it on a copy in a let, and assign the copy back if the call changes it (Rule 8.18).); for an array’s methods, TS8099 for another method (".filter" is not one of an array's methods here, which are map, forEach, some, every and reduce. …), for map on an array with no size and for a function handed that array, and TS8019 ARITY_MISMATCH for reduce with no value to start from on one and for a thisArg; pinned by src/compiler/ts/higher-order.test.ts and src/compiler/ts/array-methods.test.ts, which hold WGSL, GLSL ES 3.00, the CPU oracle, the codegen and the debugger to one value for each form, on a fixed-size array and on a runtime-sized storage one; examples/higher-order.shade.ts and examples/array-methods.shade.ts in the compile gate; the light-list journey on WebGPU.
커밋 c66579bf에서 이 규칙을 확인하는 파일입니다. 파일마다 규칙을 처음 가리키는 줄로 연결했습니다.
설명하는 절
커밋 c66579bf의 표면 문서에서 이 규칙을 설명하는 절입니다.
적용하는 오류 코드
규칙이 Enforced by 항목에서 가리키는 진단 코드와, 레지스트리 설명에서 이 규칙을 가리키는 진단 코드입니다.
-
TS8002UNKNOWN_TYPE - A type name nothing declares, said once for the file wherever it is written (a parameter, a claim, a type argument,
B<vec3<Foo>>included, an alias, animplementsclause, the base a class or an interface extends, a body no call lowers), a type the library declares for TypeScript alone (Number) where the file declares none of its name, or a type this surface does not take in a position the mapper reads (Rule 2.1). -
TS8003TYPE_MISMATCH - 연산자나 조건, 선언, 반환, 인자처럼 두 값이 만나는 자리에서 타입이 서로 맞지 않거나, 컴파일러가 접는 상수를 WGSL이 거부합니다. 31을 넘는 시프트 양, 0인 제수, 서로 엇갈린 clamp 경계, 범위를 넘는 f32 값이 그렇습니다.
-
TS8019ARITY_MISMATCH - Wrong number of arguments, elements, or fields at a call or constructor site.
-
TS8020FUNCTION_SHAPE - A function declaration or parameter shape TypeShade does not support (missing name or body, optional/rest/destructured parameter).
-
TS8031RECURSION - A call cycle: a function that reaches itself, directly or through other functions.
-
TS8099UNSUPPORTED UNSUPPORTED(TS8099) is the one deliberate exception to "sequential": it is the catch-all for a diagnostic whose site does not yet deserve its own code, so it stays parked past the sequential range instead of at its head.
함께 보기
소스
커밋 c66579bf의 규칙 원문:
docs/language-design.md:802(설계 문서)reqs/rules/RULE-0818.md(추적 항목)