[WHLSL] Educate the property resolver about IndexExpressions
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Jun 2019 02:17:15 +0000 (02:17 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Jun 2019 02:17:15 +0000 (02:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198399

Reviewed by Saam Barati.

Source/WebCore:

This is part one of two patches which will allow buffers to work. This patch
adds support in the property resolver for index expressions. Index expressions
get turned into calls to "getter indexers", "setter indexers", or "ander
indexers". They work almost identically to dot expressions, except there is an
extra "index" expression which gets turned into an extra argument to those
functions.

There's actually a bit of a trick here. Let's say we need to run a getter and
a setter separately (e.g. "foo[3]++;"). The index expression can't be duplicated
for both the getter and the setter (e.g. the functions are
int operator[](Foo, uint) and Foo operator[]=(Foo, uint, int), and we aren't
allowed to execute the index expression multiple times. Consider if that "3"
in the example is actually "bar()" with some side effect. So, we have to run
the index expression once at the correct time, and save its result to a temporary
variable, and then pass in the temporary variable into the getter and setter.

So, if the code says "foo[bar()][baz()] = quux();" the following sequence of
functions get run:

- bar()
- operator[](Foo, uint)
- baz()
- quux()
- operator[]=(OtherType, uint, OtherOtherType)
- operator[]=(Foo, uint, OtherType)

The next patch will modify the WebGPU JavaScript implementation to send buffer
lengths to the shader, and for the shader compiler to correctly unpack this
information and place it inside the array references. That should be everything
that's needed to get buffers to work. After that, hooking up compute should be
fairly trivial.

Tests: webgpu/propertyresolver/ander-abstract-lvalue.html
       webgpu/propertyresolver/ander-lvalue-3-levels.html
       webgpu/propertyresolver/ander-lvalue.html
       webgpu/propertyresolver/ander.html
       webgpu/propertyresolver/getter.html
       webgpu/propertyresolver/indexer-ander-abstract-lvalue.html
       webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html
       webgpu/propertyresolver/indexer-ander-lvalue.html
       webgpu/propertyresolver/indexer-ander.html
       webgpu/propertyresolver/indexer-getter.html
       webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html
       webgpu/propertyresolver/indexer-setter-abstract-lvalue.html
       webgpu/propertyresolver/indexer-setter-lvalue.html
       webgpu/propertyresolver/indexer-setter.html
       webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html
       webgpu/propertyresolver/setter-abstract-lvalue.html
       webgpu/propertyresolver/setter-lvalue.html

* Modules/webgpu/WHLSL/AST/WHLSLAddressSpace.h:
(WebCore::WHLSL::AST::toString):
* Modules/webgpu/WHLSL/AST/WHLSLEntryPointType.h:
(WebCore::WHLSL::AST::toString):
* Modules/webgpu/WHLSL/AST/WHLSLIndexExpression.h:
(WebCore::WHLSL::AST::IndexExpression::takeIndex):
* Modules/webgpu/WHLSL/AST/WHLSLReferenceType.h:
* Modules/webgpu/WHLSL/Metal/WHLSLNativeFunctionWriter.cpp:
(WebCore::WHLSL::Metal::writeNativeFunction):
(WebCore::WHLSL::Metal::convertAddressSpace): Deleted.
* Modules/webgpu/WHLSL/WHLSLChecker.cpp:
(WebCore::WHLSL::checkOperatorOverload):
(WebCore::WHLSL::Checker::finishVisiting):
(WebCore::WHLSL::Checker::visit):
* Modules/webgpu/WHLSL/WHLSLInferTypes.h:
* Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp:
(WebCore::WHLSL::PropertyResolver::visit):
(WebCore::WHLSL::setterCall):
(WebCore::WHLSL::getterCall):
(WebCore::WHLSL::modify):
(WebCore::WHLSL::PropertyResolver::simplifyRightValue):
(WebCore::WHLSL::LeftValueSimplifier::finishVisiting):
(WebCore::WHLSL::LeftValueSimplifier::visit):
* Modules/webgpu/WHLSL/WHLSLStandardLibrary.txt:
* Modules/webgpu/WHLSL/WHLSLSynthesizeStructureAccessors.cpp:
(WebCore::WHLSL::synthesizeStructureAccessors):

LayoutTests:

* webgpu/propertyresolver/ander-abstract-lvalue-expected.html: Added.
* webgpu/propertyresolver/ander-abstract-lvalue.html: Added.
* webgpu/propertyresolver/ander-expected.html: Added.
* webgpu/propertyresolver/ander-lvalue-3-levels-expected.html: Added.
* webgpu/propertyresolver/ander-lvalue-3-levels.html: Added.
* webgpu/propertyresolver/ander-lvalue-expected.html: Added.
* webgpu/propertyresolver/ander-lvalue.html: Added.
* webgpu/propertyresolver/ander.html: Added.
* webgpu/propertyresolver/getter-expected.html: Added.
* webgpu/propertyresolver/getter.html: Added.
* webgpu/propertyresolver/indexer-ander-abstract-lvalue-expected.html: Added.
* webgpu/propertyresolver/indexer-ander-abstract-lvalue.html: Added.
* webgpu/propertyresolver/indexer-ander-expected.html: Added.
* webgpu/propertyresolver/indexer-ander-lvalue-3-levels-expected.html: Added.
* webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html: Added.
* webgpu/propertyresolver/indexer-ander-lvalue-expected.html: Added.
* webgpu/propertyresolver/indexer-ander-lvalue.html: Added.
* webgpu/propertyresolver/indexer-ander.html: Added.
* webgpu/propertyresolver/indexer-getter-expected.html: Added.
* webgpu/propertyresolver/indexer-getter.html: Added.
* webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels-expected.html: Added.
* webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html: Added.
* webgpu/propertyresolver/indexer-setter-abstract-lvalue-expected.html: Added.
* webgpu/propertyresolver/indexer-setter-abstract-lvalue.html: Added.
* webgpu/propertyresolver/indexer-setter-expected.html: Added.
* webgpu/propertyresolver/indexer-setter-lvalue-expected.html: Added.
* webgpu/propertyresolver/indexer-setter-lvalue.html: Added.
* webgpu/propertyresolver/indexer-setter.html: Added.
* webgpu/propertyresolver/setter-abstract-lvalue-3-levels-expected.html: Added.
* webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html: Added.
* webgpu/propertyresolver/setter-abstract-lvalue-expected.html: Added.
* webgpu/propertyresolver/setter-abstract-lvalue.html: Added.
* webgpu/propertyresolver/setter-lvalue-expected.html: Added.
* webgpu/propertyresolver/setter-lvalue.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@246138 268f45cc-cd09-0410-ab3c-d52691b4dbfc

46 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/propertyresolver/ander-abstract-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander-abstract-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander-lvalue-3-levels-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander-lvalue-3-levels.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/ander.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/getter-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/getter.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-abstract-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-abstract-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-3-levels-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-ander.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-getter-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-getter.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/indexer-setter.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-3-levels-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/setter-lvalue-expected.html [new file with mode: 0644]
LayoutTests/webgpu/propertyresolver/setter-lvalue.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLAddressSpace.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLEntryPointType.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLIndexExpression.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLReferenceType.h
Source/WebCore/Modules/webgpu/WHLSL/Metal/WHLSLNativeFunctionWriter.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLChecker.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLInferTypes.h
Source/WebCore/Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLStandardLibrary.txt
Source/WebCore/Modules/webgpu/WHLSL/WHLSLSynthesizeStructureAccessors.cpp

index 41b14fc..3bf1bae 100644 (file)
@@ -1,3 +1,45 @@
+2019-06-05  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WHLSL] Educate the property resolver about IndexExpressions
+        https://bugs.webkit.org/show_bug.cgi?id=198399
+
+        Reviewed by Saam Barati.
+
+        * webgpu/propertyresolver/ander-abstract-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/ander-abstract-lvalue.html: Added.
+        * webgpu/propertyresolver/ander-expected.html: Added.
+        * webgpu/propertyresolver/ander-lvalue-3-levels-expected.html: Added.
+        * webgpu/propertyresolver/ander-lvalue-3-levels.html: Added.
+        * webgpu/propertyresolver/ander-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/ander-lvalue.html: Added.
+        * webgpu/propertyresolver/ander.html: Added.
+        * webgpu/propertyresolver/getter-expected.html: Added.
+        * webgpu/propertyresolver/getter.html: Added.
+        * webgpu/propertyresolver/indexer-ander-abstract-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/indexer-ander-abstract-lvalue.html: Added.
+        * webgpu/propertyresolver/indexer-ander-expected.html: Added.
+        * webgpu/propertyresolver/indexer-ander-lvalue-3-levels-expected.html: Added.
+        * webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html: Added.
+        * webgpu/propertyresolver/indexer-ander-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/indexer-ander-lvalue.html: Added.
+        * webgpu/propertyresolver/indexer-ander.html: Added.
+        * webgpu/propertyresolver/indexer-getter-expected.html: Added.
+        * webgpu/propertyresolver/indexer-getter.html: Added.
+        * webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels-expected.html: Added.
+        * webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html: Added.
+        * webgpu/propertyresolver/indexer-setter-abstract-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/indexer-setter-abstract-lvalue.html: Added.
+        * webgpu/propertyresolver/indexer-setter-expected.html: Added.
+        * webgpu/propertyresolver/indexer-setter-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/indexer-setter-lvalue.html: Added.
+        * webgpu/propertyresolver/indexer-setter.html: Added.
+        * webgpu/propertyresolver/setter-abstract-lvalue-3-levels-expected.html: Added.
+        * webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html: Added.
+        * webgpu/propertyresolver/setter-abstract-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/setter-abstract-lvalue.html: Added.
+        * webgpu/propertyresolver/setter-lvalue-expected.html: Added.
+        * webgpu/propertyresolver/setter-lvalue.html: Added.
+
 2019-06-05  Daniel Bates  <dabates@apple.com>
 
         [CSP] Data URLs should inherit their CSP policy
diff --git a/LayoutTests/webgpu/propertyresolver/ander-abstract-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/ander-abstract-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander-abstract-lvalue.html b/LayoutTests/webgpu/propertyresolver/ander-abstract-lvalue.html
new file mode 100644 (file)
index 0000000..ce14a68
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&.y(thread Foo* f) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator.w(Bar b) {
+    return b.z;
+}
+
+Bar operator.w=(Bar b, Foo a) {
+    b.z = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b.w.y = 1.0;
+    return float4(b.w.y, b.w.y, b.w.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander-expected.html b/LayoutTests/webgpu/propertyresolver/ander-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander-lvalue-3-levels-expected.html b/LayoutTests/webgpu/propertyresolver/ander-lvalue-3-levels-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander-lvalue-3-levels.html b/LayoutTests/webgpu/propertyresolver/ander-lvalue-3-levels.html
new file mode 100644 (file)
index 0000000..b344609
--- /dev/null
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&.y(thread Foo* f) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+thread Foo* operator&.w(thread Bar* b) {
+    return &b->z;
+}
+
+struct Baz {
+    Bar p;
+}
+
+thread Bar* operator&.q(thread Baz* b) {
+    return &b->p;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Baz b;
+    b.q.w.y = 1.0;
+    return float4(b.q.w.y, b.q.w.y, b.q.w.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/ander-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander-lvalue.html b/LayoutTests/webgpu/propertyresolver/ander-lvalue.html
new file mode 100644 (file)
index 0000000..31f23f9
--- /dev/null
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&.y(thread Foo* f) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+thread Foo* operator&.w(thread Bar* b) {
+    return &b->z;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b.w.y = 1.0;
+    return float4(b.w.y, b.w.y, b.w.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/ander.html b/LayoutTests/webgpu/propertyresolver/ander.html
new file mode 100644 (file)
index 0000000..f7b60e6
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&.y(thread Foo* f) {
+    return &f->x;
+}
+
+Foo generateFoo() {
+    Foo f;
+    f.x = 1.0;
+    return f;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    return float4(generateFoo().y, generateFoo().y, generateFoo().y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/getter-expected.html b/LayoutTests/webgpu/propertyresolver/getter-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/getter.html b/LayoutTests/webgpu/propertyresolver/getter.html
new file mode 100644 (file)
index 0000000..cc5cba0
--- /dev/null
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator.y(Foo f) {
+    return f.x;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Foo f;
+    f.x = 1.0;
+    return float4(f.y, f.y, f.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-abstract-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-abstract-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-abstract-lvalue.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-abstract-lvalue.html
new file mode 100644 (file)
index 0000000..afce1af
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&[](thread Foo* f, uint index) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator[](Bar b, uint index) {
+    return b.z;
+}
+
+Bar operator[]=(Bar b, uint index, Foo a) {
+    b.z = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b[2][3] = 1.0;
+    return float4(b[2][3], b[2][3], b[2][3], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-3-levels-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-3-levels-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html
new file mode 100644 (file)
index 0000000..c3fec65
--- /dev/null
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&[](thread Foo* f, uint index) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+thread Foo* operator&[](thread Bar* b, uint index) {
+    return &b->z;
+}
+
+struct Baz {
+    Bar p;
+}
+
+thread Bar* operator&[](thread Baz* b, uint index) {
+    return &b->p;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Baz b;
+    b[1][2][3] = 1.0;
+    return float4(b[1][2][3], b[1][2][3], b[1][2][3], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue.html b/LayoutTests/webgpu/propertyresolver/indexer-ander-lvalue.html
new file mode 100644 (file)
index 0000000..354ba5e
--- /dev/null
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&[](thread Foo* f, uint index) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+thread Foo* operator&[](thread Bar* b, uint index) {
+    return &b->z;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b[1][2] = 1.0;
+    return float4(b[1][2], b[1][2], b[1][2], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-ander.html b/LayoutTests/webgpu/propertyresolver/indexer-ander.html
new file mode 100644 (file)
index 0000000..c661114
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&[](thread Foo* f, uint index) {
+    return &f->x;
+}
+
+Foo generateFoo() {
+    Foo f;
+    f.x = 1.0;
+    return f;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    return float4(generateFoo()[0], generateFoo()[1], generateFoo()[2], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-getter-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-getter-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-getter.html b/LayoutTests/webgpu/propertyresolver/indexer-getter.html
new file mode 100644 (file)
index 0000000..df16838
--- /dev/null
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator[](Foo f, uint index) {
+    return float(index);
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Foo f;
+    return float4(f[1], f[1], f[1], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html
new file mode 100644 (file)
index 0000000..716f01d
--- /dev/null
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator[](Foo f, uint index) {
+    return f.x;
+}
+
+Foo operator[]=(Foo f, uint index, float x) {
+    f.x = x;
+    return f;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator[](Bar b, uint index) {
+    return b.z;
+}
+
+Bar operator[]=(Bar b, uint index, Foo a) {
+    b.z = a;
+    return b;
+}
+
+struct Baz {
+    Bar p;
+}
+
+Bar operator[](Baz b, uint index) {
+    return b.p;
+}
+
+Baz operator[]=(Baz b, uint index, Bar a) {
+    b.p = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Baz b;
+    b[2][3][4] = 1.0;
+    return float4(b[2][3][4], b[2][3][4], b[2][3][4], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-abstract-lvalue.html
new file mode 100644 (file)
index 0000000..551404f
--- /dev/null
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator[](Foo f, uint index) {
+    return f.x;
+}
+
+Foo operator[]=(Foo f, uint index, float x) {
+    f.x = x;
+    return f;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator[](Bar b, uint index) {
+    return b.z;
+}
+
+Bar operator[]=(Bar b, uint index, Foo a) {
+    b.z = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b[2][3] = 1.0;
+    return float4(b[2][3], b[2][3], b[2][3], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter-lvalue.html b/LayoutTests/webgpu/propertyresolver/indexer-setter-lvalue.html
new file mode 100644 (file)
index 0000000..fd9234d
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator[](Foo f, uint index) {
+    return f.x;
+}
+
+Foo operator[]=(Foo f, uint index, float x) {
+    f.x = x;
+    return f;
+}
+
+struct Bar {
+    Foo z;
+}
+
+thread Foo* operator&[](thread Bar* b, uint index) {
+    return &b->z;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b[2][3] = 1.0;
+    return float4(b[2][3], b[2][3], b[2][3], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/indexer-setter.html b/LayoutTests/webgpu/propertyresolver/indexer-setter.html
new file mode 100644 (file)
index 0000000..61da193
--- /dev/null
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+thread float* operator&[](thread Foo* f, uint index) {
+    return &f->x;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator[](Bar b, uint index) {
+    return b.z;
+}
+
+Bar operator[]=(Bar b, uint index, Foo a) {
+    b.z = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    Foo foo;
+    foo.x = 1.0;
+    b[1] = foo;
+    foo = b[1];
+    return float4(foo.x, foo.x, foo.x, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-3-levels-expected.html b/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-3-levels-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html b/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html
new file mode 100644 (file)
index 0000000..6e912f5
--- /dev/null
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator.y(Foo f) {
+    return f.x;
+}
+
+Foo operator.y=(Foo f, float x) {
+    f.x = x;
+    return f;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator.w(Bar b) {
+    return b.z;
+}
+
+Bar operator.w=(Bar b, Foo a) {
+    b.z = a;
+    return b;
+}
+
+struct Baz {
+    Bar p;
+}
+
+Bar operator.q(Baz b) {
+    return b.p;
+}
+
+Baz operator.q=(Baz b, Bar a) {
+    b.p = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Baz b;
+    b.q.w.y = 1.0;
+    return float4(b.q.w.y, b.q.w.y, b.q.w.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue.html b/LayoutTests/webgpu/propertyresolver/setter-abstract-lvalue.html
new file mode 100644 (file)
index 0000000..0ddc7b4
--- /dev/null
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator.y(Foo f) {
+    return f.x;
+}
+
+Foo operator.y=(Foo f, float x) {
+    f.x = x;
+    return f;
+}
+
+struct Bar {
+    Foo z;
+}
+
+Foo operator.w(Bar b) {
+    return b.z;
+}
+
+Bar operator.w=(Bar b, Foo a) {
+    b.z = a;
+    return b;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b.w.y = 1.0;
+    return float4(b.w.y, b.w.y, b.w.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/setter-lvalue-expected.html b/LayoutTests/webgpu/propertyresolver/setter-lvalue-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/propertyresolver/setter-lvalue.html b/LayoutTests/webgpu/propertyresolver/setter-lvalue.html
new file mode 100644 (file)
index 0000000..9a9b4f2
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0)) : SV_Position {
+    return position;
+}
+
+struct Foo {
+    float x;
+}
+
+float operator.y(Foo f) {
+    return f.x;
+}
+
+Foo operator.y=(Foo f, float x) {
+    f.x = x;
+    return f;
+}
+
+struct Bar {
+    Foo z;
+}
+
+thread Foo* operator&.w(thread Bar* b) {
+    return &b->z;
+}
+
+fragment float4 fragmentShader() : SV_Target 0 {
+    Bar b;
+    b.w.y = 1.0;
+    return float4(b.w.y, b.w.y, b.w.y, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const pipelineLayoutDescriptor = {bindGroupLayouts: []};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBufferArrayBuffer = await vertexBuffer.mapWriteAsync();
+    const vertexBufferFloat32Array = new Float32Array(vertexBufferArrayBuffer);
+    vertexBufferFloat32Array[0] = -0.5;
+    vertexBufferFloat32Array[1] = -0.5;
+    vertexBufferFloat32Array[2] = 1.0;
+    vertexBufferFloat32Array[3] = 1;
+    vertexBufferFloat32Array[4] = -0.5;
+    vertexBufferFloat32Array[5] = 0.5;
+    vertexBufferFloat32Array[6] = 1.0;
+    vertexBufferFloat32Array[7] = 1;
+    vertexBufferFloat32Array[8] = 0.5;
+    vertexBufferFloat32Array[9] = -0.5;
+    vertexBufferFloat32Array[10] = 1.0;
+    vertexBufferFloat32Array[11] = 1;
+    vertexBufferFloat32Array[12] = 0.5;
+    vertexBufferFloat32Array[13] = 0.5;
+    vertexBufferFloat32Array[14] = 1.0;
+    vertexBufferFloat32Array[15] = 1;
+    vertexBuffer.unmap();
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
index 5bdb885..e472654 100644 (file)
@@ -1,3 +1,87 @@
+2019-06-05  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WHLSL] Educate the property resolver about IndexExpressions
+        https://bugs.webkit.org/show_bug.cgi?id=198399
+
+        Reviewed by Saam Barati.
+
+        This is part one of two patches which will allow buffers to work. This patch
+        adds support in the property resolver for index expressions. Index expressions
+        get turned into calls to "getter indexers", "setter indexers", or "ander
+        indexers". They work almost identically to dot expressions, except there is an
+        extra "index" expression which gets turned into an extra argument to those
+        functions.
+
+        There's actually a bit of a trick here. Let's say we need to run a getter and
+        a setter separately (e.g. "foo[3]++;"). The index expression can't be duplicated
+        for both the getter and the setter (e.g. the functions are
+        int operator[](Foo, uint) and Foo operator[]=(Foo, uint, int), and we aren't
+        allowed to execute the index expression multiple times. Consider if that "3"
+        in the example is actually "bar()" with some side effect. So, we have to run
+        the index expression once at the correct time, and save its result to a temporary
+        variable, and then pass in the temporary variable into the getter and setter.
+
+        So, if the code says "foo[bar()][baz()] = quux();" the following sequence of
+        functions get run:
+
+        - bar()
+        - operator[](Foo, uint)
+        - baz()
+        - quux()
+        - operator[]=(OtherType, uint, OtherOtherType)
+        - operator[]=(Foo, uint, OtherType)
+
+        The next patch will modify the WebGPU JavaScript implementation to send buffer
+        lengths to the shader, and for the shader compiler to correctly unpack this
+        information and place it inside the array references. That should be everything
+        that's needed to get buffers to work. After that, hooking up compute should be
+        fairly trivial.
+
+        Tests: webgpu/propertyresolver/ander-abstract-lvalue.html
+               webgpu/propertyresolver/ander-lvalue-3-levels.html
+               webgpu/propertyresolver/ander-lvalue.html
+               webgpu/propertyresolver/ander.html
+               webgpu/propertyresolver/getter.html
+               webgpu/propertyresolver/indexer-ander-abstract-lvalue.html
+               webgpu/propertyresolver/indexer-ander-lvalue-3-levels.html
+               webgpu/propertyresolver/indexer-ander-lvalue.html
+               webgpu/propertyresolver/indexer-ander.html
+               webgpu/propertyresolver/indexer-getter.html
+               webgpu/propertyresolver/indexer-setter-abstract-lvalue-3-levels.html
+               webgpu/propertyresolver/indexer-setter-abstract-lvalue.html
+               webgpu/propertyresolver/indexer-setter-lvalue.html
+               webgpu/propertyresolver/indexer-setter.html
+               webgpu/propertyresolver/setter-abstract-lvalue-3-levels.html
+               webgpu/propertyresolver/setter-abstract-lvalue.html
+               webgpu/propertyresolver/setter-lvalue.html
+
+        * Modules/webgpu/WHLSL/AST/WHLSLAddressSpace.h:
+        (WebCore::WHLSL::AST::toString):
+        * Modules/webgpu/WHLSL/AST/WHLSLEntryPointType.h:
+        (WebCore::WHLSL::AST::toString):
+        * Modules/webgpu/WHLSL/AST/WHLSLIndexExpression.h:
+        (WebCore::WHLSL::AST::IndexExpression::takeIndex):
+        * Modules/webgpu/WHLSL/AST/WHLSLReferenceType.h:
+        * Modules/webgpu/WHLSL/Metal/WHLSLNativeFunctionWriter.cpp:
+        (WebCore::WHLSL::Metal::writeNativeFunction):
+        (WebCore::WHLSL::Metal::convertAddressSpace): Deleted.
+        * Modules/webgpu/WHLSL/WHLSLChecker.cpp:
+        (WebCore::WHLSL::checkOperatorOverload):
+        (WebCore::WHLSL::Checker::finishVisiting):
+        (WebCore::WHLSL::Checker::visit):
+        * Modules/webgpu/WHLSL/WHLSLInferTypes.h:
+        * Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp:
+        (WebCore::WHLSL::PropertyResolver::visit):
+        (WebCore::WHLSL::setterCall):
+        (WebCore::WHLSL::getterCall):
+        (WebCore::WHLSL::modify):
+        (WebCore::WHLSL::PropertyResolver::simplifyRightValue):
+        (WebCore::WHLSL::LeftValueSimplifier::finishVisiting):
+        (WebCore::WHLSL::LeftValueSimplifier::visit):
+        * Modules/webgpu/WHLSL/WHLSLStandardLibrary.txt:
+        * Modules/webgpu/WHLSL/WHLSLSynthesizeStructureAccessors.cpp:
+        (WebCore::WHLSL::synthesizeStructureAccessors):
+
 2019-06-05  Robin Morisset  <rmorisset@apple.com>
 
         [WHLSL] Parsing and lexing the standard library is slow
index 425d134..aba33d5 100644 (file)
@@ -43,7 +43,7 @@ enum class AddressSpace : uint8_t {
     Thread
 };
 
-static ALWAYS_INLINE String toString(AddressSpace addressSpace)
+ALWAYS_INLINE String toString(AddressSpace addressSpace)
 {
     switch (addressSpace) {
     case AddressSpace::Constant:
index 65f6873..b45424f 100644 (file)
@@ -41,7 +41,7 @@ enum class EntryPointType : uint8_t {
     Compute,
 };
 
-static ALWAYS_INLINE String toString(EntryPointType type)
+ALWAYS_INLINE String toString(EntryPointType type)
 {
     switch (type) {
     case EntryPointType::Vertex:
index aa197b5..5e49116 100644 (file)
@@ -59,15 +59,16 @@ public:
 
     String setterFunctionName() const override
     {
-        return "operator&[]"_str;
+        return "operator[]="_str;
     }
 
     String anderFunctionName() const override
     {
-        return "operator[]="_str;
+        return "operator&[]"_str;
     }
 
     Expression& indexExpression() { return m_index; }
+    UniqueRef<Expression> takeIndex() { return WTFMove(m_index); }
 
 private:
     UniqueRef<Expression> m_index;
index e7e514f..0cff9ff 100644 (file)
@@ -53,7 +53,7 @@ public:
     ReferenceType(const ReferenceType&) = delete;
     ReferenceType(ReferenceType&&) = default;
 
-    bool isReferenceType() const override { return false; }
+    bool isReferenceType() const override { return true; }
 
     AddressSpace addressSpace() const { return m_addressSpace; }
     const UnnamedType& elementType() const { return m_elementType; }
index 38e7a71..69dffdf 100644 (file)
@@ -63,21 +63,6 @@ static String mapFunctionName(String& functionName)
     return functionName;
 }
 
-static String convertAddressSpace(AST::AddressSpace addressSpace)
-{
-    switch (addressSpace) {
-    case AST::AddressSpace::Constant:
-        return "constant"_str;
-    case AST::AddressSpace::Device:
-        return "device"_str;
-    case AST::AddressSpace::Threadgroup:
-        return "threadgroup"_str;
-    default:
-        ASSERT(addressSpace == AST::AddressSpace::Thread);
-        return "thread"_str;
-    }
-}
-
 static String atomicName(String input)
 {
     if (input == "Add")
@@ -375,7 +360,7 @@ String writeNativeFunction(AST::NativeFunctionDeclaration& nativeFunctionDeclara
             auto& fourthArgumentPointer = downcast<AST::PointerType>(*nativeFunctionDeclaration.parameters()[3]->type());
             auto fourthArgumentAddressSpace = fourthArgumentPointer.addressSpace();
             auto fourthArgumentPointee = typeNamer.mangledNameForType(fourthArgumentPointer.elementType());
-            stringBuilder.append(makeString("void ", outputFunctionName, '(', convertAddressSpace(firstArgumentAddressSpace), ' ', firstArgumentPointee, "* object, ", secondArgument, " compare, ", thirdArgument, " desired, ", convertAddressSpace(fourthArgumentAddressSpace), ' ', fourthArgumentPointee, "* out) {\n"));
+            stringBuilder.append(makeString("void ", outputFunctionName, '(', toString(firstArgumentAddressSpace), ' ', firstArgumentPointee, "* object, ", secondArgument, " compare, ", thirdArgument, " desired, ", toString(fourthArgumentAddressSpace), ' ', fourthArgumentPointee, "* out) {\n"));
             stringBuilder.append("    atomic_compare_exchange_weak_explicit(object, &compare, desired, memory_order_relaxed);\n");
             stringBuilder.append("    *out = compare;\n");
             stringBuilder.append("}\n");
@@ -393,7 +378,7 @@ String writeNativeFunction(AST::NativeFunctionDeclaration& nativeFunctionDeclara
         auto thirdArgumentAddressSpace = thirdArgumentPointer.addressSpace();
         auto thirdArgumentPointee = typeNamer.mangledNameForType(thirdArgumentPointer.elementType());
         auto name = atomicName(nativeFunctionDeclaration.name().substring("Interlocked"_str.length()));
-        stringBuilder.append(makeString("void ", outputFunctionName, '(', convertAddressSpace(firstArgumentAddressSpace), ' ', firstArgumentPointee, "* object, ", secondArgument, " operand, ", convertAddressSpace(thirdArgumentAddressSpace), ' ', thirdArgumentPointee, "* out) {\n"));
+        stringBuilder.append(makeString("void ", outputFunctionName, '(', toString(firstArgumentAddressSpace), ' ', firstArgumentPointee, "* object, ", secondArgument, " operand, ", toString(thirdArgumentAddressSpace), ' ', thirdArgumentPointee, "* out) {\n"));
         stringBuilder.append(makeString("    *out = atomic_fetch_", name, "_explicit(object, operand, memory_order_relaxed);\n"));
         stringBuilder.append("}\n");
         return stringBuilder.toString();
index 11d631e..88e7553 100644 (file)
@@ -36,6 +36,7 @@
 #include "WHLSLDereferenceExpression.h"
 #include "WHLSLDoWhileLoop.h"
 #include "WHLSLDotExpression.h"
+#include "WHLSLEntryPointType.h"
 #include "WHLSLForLoop.h"
 #include "WHLSLGatherEntryPointItems.h"
 #include "WHLSLIfStatement.h"
@@ -354,7 +355,7 @@ static bool checkOperatorOverload(const AST::FunctionDefinition& functionDefinit
         Vector<ResolvingType> argumentTypes;
         Vector<std::reference_wrapper<ResolvingType>> argumentTypeReferences;
         for (size_t i = 0; i < numExpectedParameters - 1; ++i)
-            argumentTypes.append((*functionDefinition.parameters()[0]->type())->clone());
+            argumentTypes.append((*functionDefinition.parameters()[i]->type())->clone());
         for (auto& argumentType : argumentTypes)
             argumentTypeReferences.append(argumentType);
         auto* overload = resolveFunctionOverloadImpl(*getterFuncs, argumentTypeReferences, nullptr);
@@ -491,6 +492,8 @@ private:
     void visit(AST::TernaryExpression&) override;
     void visit(AST::CallExpression&) override;
 
+    void finishVisiting(AST::PropertyAccessExpression&, ResolvingType* additionalArgumentType = nullptr);
+
     HashMap<AST::Expression*, ResolvingType> m_typeMap;
     HashMap<AST::Expression*, AST::TypeAnnotation> m_typeAnnotations;
     HashSet<String> m_vertexEntryPoints;
@@ -966,9 +969,9 @@ void Checker::visit(AST::MakeArrayReferenceExpression& makeArrayReferenceExpress
     assignType(makeArrayReferenceExpression, makeUniqueRef<AST::ArrayReferenceType>(Lexer::Token(makeArrayReferenceExpression.origin()), *leftAddressSpace, leftValueType->clone()));
 }
 
-void Checker::visit(AST::DotExpression& dotExpression)
+void Checker::finishVisiting(AST::PropertyAccessExpression& propertyAccessExpression, ResolvingType* additionalArgumentType)
 {
-    auto baseInfo = recurseAndGetInfo(dotExpression.base());
+    auto baseInfo = recurseAndGetInfo(propertyAccessExpression.base());
     if (!baseInfo)
         return;
     auto baseUnnamedType = commit(baseInfo->resolvingType);
@@ -979,65 +982,89 @@ void Checker::visit(AST::DotExpression& dotExpression)
     AST::UnnamedType* getterReturnType = nullptr;
     {
         Vector<std::reference_wrapper<ResolvingType>> getterArgumentTypes { baseInfo->resolvingType };
-        getterFunction = resolveFunctionOverloadImpl(dotExpression.possibleGetterOverloads(), getterArgumentTypes, nullptr);
-        if (getterFunction)
+        if (additionalArgumentType)
+            getterArgumentTypes.append(*additionalArgumentType);
+        if ((getterFunction = resolveFunctionOverloadImpl(propertyAccessExpression.possibleGetterOverloads(), getterArgumentTypes, nullptr)))
             getterReturnType = &getterFunction->type();
     }
 
     AST::FunctionDeclaration* anderFunction = nullptr;
     AST::UnnamedType* anderReturnType = nullptr;
-    if (auto leftAddressSpace = baseInfo->typeAnnotation.leftAddressSpace()) {
-        auto argumentType = makeUniqueRef<AST::PointerType>(Lexer::Token(dotExpression.origin()), *leftAddressSpace, baseUnnamedType->get().clone());
-        Vector<std::reference_wrapper<ResolvingType>> anderArgumentTypes { baseInfo->resolvingType };
-        anderFunction = resolveFunctionOverloadImpl(dotExpression.possibleAnderOverloads(), anderArgumentTypes, nullptr);
-        if (anderFunction)
+    auto leftAddressSpace = baseInfo->typeAnnotation.leftAddressSpace();
+    if (leftAddressSpace) {
+        ResolvingType argumentType = { makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), *leftAddressSpace, baseUnnamedType->get().clone()) };
+        Vector<std::reference_wrapper<ResolvingType>> anderArgumentTypes { argumentType };
+        if (additionalArgumentType)
+            anderArgumentTypes.append(*additionalArgumentType);
+        if ((anderFunction = resolveFunctionOverloadImpl(propertyAccessExpression.possibleAnderOverloads(), anderArgumentTypes, nullptr)))
             anderReturnType = &downcast<AST::PointerType>(anderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
     }
 
     AST::FunctionDeclaration* threadAnderFunction = nullptr;
     AST::UnnamedType* threadAnderReturnType = nullptr;
     {
-        auto argumentType = makeUniqueRef<AST::PointerType>(Lexer::Token(dotExpression.origin()), AST::AddressSpace::Thread, baseUnnamedType->get().clone());
-        Vector<std::reference_wrapper<ResolvingType>> threadAnderArgumentTypes { baseInfo->resolvingType };
-        threadAnderFunction = resolveFunctionOverloadImpl(dotExpression.possibleAnderOverloads(), threadAnderArgumentTypes, nullptr);
-        if (threadAnderFunction)
+        ResolvingType argumentType = { makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), AST::AddressSpace::Thread, baseUnnamedType->get().clone()) };
+        Vector<std::reference_wrapper<ResolvingType>> threadAnderArgumentTypes { argumentType };
+        if (additionalArgumentType)
+            threadAnderArgumentTypes.append(*additionalArgumentType);
+        if ((threadAnderFunction = resolveFunctionOverloadImpl(propertyAccessExpression.possibleAnderOverloads(), threadAnderArgumentTypes, nullptr)))
             threadAnderReturnType = &downcast<AST::PointerType>(threadAnderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
     }
 
-    if (!getterFunction && !anderFunction) {
+    if (leftAddressSpace && !anderFunction && !getterFunction) {
+        setError();
+        return;
+    }
+
+    if (!leftAddressSpace && !threadAnderFunction && !getterFunction) {
         setError();
         return;
     }
-    if (getterFunction && anderFunction) {
+
+    if (threadAnderFunction && getterFunction) {
         setError();
         return;
     }
+
     if (anderFunction && threadAnderFunction && !matches(*anderReturnType, *threadAnderReturnType)) {
         setError();
         return;
     }
 
-    AST::UnnamedType* fieldType = getterReturnType ? getterReturnType : anderReturnType;
+    if (getterFunction && anderFunction && !matches(*getterReturnType, *anderReturnType)) {
+        setError();
+        return;
+    }
+
+    if (getterFunction && threadAnderFunction && !matches(*getterReturnType, *threadAnderReturnType)) {
+        setError();
+        return;
+    }
+
+    AST::UnnamedType* fieldType = getterReturnType ? getterReturnType : anderReturnType ? anderReturnType : threadAnderReturnType;
 
     AST::FunctionDeclaration* setterFunction = nullptr;
     AST::UnnamedType* setterReturnType = nullptr;
     {
         ResolvingType fieldResolvingType(fieldType->clone());
-        Vector<std::reference_wrapper<ResolvingType>> setterArgumentTypes { baseInfo->resolvingType, fieldResolvingType };
-        setterFunction = resolveFunctionOverloadImpl(dotExpression.possibleSetterOverloads(), setterArgumentTypes, nullptr);
+        Vector<std::reference_wrapper<ResolvingType>> setterArgumentTypes { baseInfo->resolvingType };
+        if (additionalArgumentType)
+            setterArgumentTypes.append(*additionalArgumentType);
+        setterArgumentTypes.append(fieldResolvingType);
+        setterFunction = resolveFunctionOverloadImpl(propertyAccessExpression.possibleSetterOverloads(), setterArgumentTypes, nullptr);
         if (setterFunction)
             setterReturnType = &setterFunction->type();
     }
 
-    if (setterFunction && anderFunction) {
+    if (setterFunction && !getterFunction) {
         setError();
         return;
     }
 
-    dotExpression.setGetterFunction(getterFunction);
-    dotExpression.setAnderFunction(anderFunction);
-    dotExpression.setThreadAnderFunction(threadAnderFunction);
-    dotExpression.setSetterFunction(setterFunction);
+    propertyAccessExpression.setGetterFunction(getterFunction);
+    propertyAccessExpression.setAnderFunction(anderFunction);
+    propertyAccessExpression.setThreadAnderFunction(threadAnderFunction);
+    propertyAccessExpression.setSetterFunction(setterFunction);
 
     AST::TypeAnnotation typeAnnotation = AST::RightValue();
     if (auto leftAddressSpace = baseInfo->typeAnnotation.leftAddressSpace()) {
@@ -1045,14 +1072,22 @@ void Checker::visit(AST::DotExpression& dotExpression)
             typeAnnotation = AST::LeftValue { *leftAddressSpace };
         else if (setterFunction)
             typeAnnotation = AST::AbstractLeftValue();
-    } else if (!baseInfo->typeAnnotation.isRightValue() && (setterFunction || anderFunction))
+    } else if (!baseInfo->typeAnnotation.isRightValue() && (setterFunction || threadAnderFunction))
         typeAnnotation = AST::AbstractLeftValue();
-    assignType(dotExpression, fieldType->clone(), WTFMove(typeAnnotation));
+    assignType(propertyAccessExpression, fieldType->clone(), WTFMove(typeAnnotation));
 }
 
-void Checker::visit(AST::IndexExpression&)
+void Checker::visit(AST::DotExpression& dotExpression)
+{
+    finishVisiting(dotExpression);
+}
+
+void Checker::visit(AST::IndexExpression& indexExpression)
 {
-    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198163 Implement this.
+    auto baseInfo = recurseAndGetInfo(indexExpression.indexExpression());
+    if (!baseInfo)
+        return;
+    finishVisiting(indexExpression, &baseInfo->resolvingType);
 }
 
 void Checker::visit(AST::VariableReference& variableReference)
@@ -1362,7 +1397,7 @@ void Checker::visit(AST::CommaExpression& commaExpression)
     if (error())
         return;
     auto lastInfo = getInfo(commaExpression.list().last());
-    forwardType(commaExpression, lastInfo->resolvingType, lastInfo->typeAnnotation);
+    forwardType(commaExpression, lastInfo->resolvingType);
 }
 
 void Checker::visit(AST::TernaryExpression& ternaryExpression)
index 173d301..19fb1ff 100644 (file)
@@ -47,7 +47,6 @@ class UnnamedType;
 bool matches(const AST::UnnamedType&, const AST::UnnamedType&);
 bool matches(const AST::NamedType&, const AST::NamedType&);
 bool matches(const AST::UnnamedType&, const AST::NamedType&);
-// FIXME: Is anyone actually using the return type here?
 Optional<UniqueRef<AST::UnnamedType>> matchAndCommit(AST::UnnamedType&, AST::ResolvableType&);
 Optional<UniqueRef<AST::UnnamedType>> matchAndCommit(AST::NamedType&, AST::ResolvableType&);
 Optional<UniqueRef<AST::UnnamedType>> matchAndCommit(AST::ResolvableType&, AST::ResolvableType&);
index fa069ca..8a5a8b3 100644 (file)
@@ -51,10 +51,11 @@ public:
 private:
     void visit(AST::FunctionDefinition&) override;
     void visit(AST::DotExpression&) override;
+    void visit(AST::IndexExpression&) override;
     void visit(AST::AssignmentExpression&) override;
     void visit(AST::ReadModifyWriteExpression&) override;
 
-    bool simplifyRightValue(AST::DotExpression&);
+    bool simplifyRightValue(AST::PropertyAccessExpression&);
     bool simplifyAbstractLeftValue(AST::AssignmentExpression&, AST::DotExpression&, UniqueRef<AST::Expression>&& right);
     void simplifyLeftValue(AST::Expression&);
 
@@ -68,6 +69,14 @@ void PropertyResolver::visit(AST::DotExpression& dotExpression)
         setError();
 }
 
+void PropertyResolver::visit(AST::IndexExpression& indexExpression)
+{
+    checkErrorAndVisit(indexExpression.indexExpression());
+    // Unless we're inside an AssignmentExpression or a ReadModifyWriteExpression, we're a right value.
+    if (!simplifyRightValue(indexExpression))
+        setError();
+}
+
 void PropertyResolver::visit(AST::FunctionDefinition& functionDefinition)
 {
     Visitor::visit(functionDefinition);
@@ -75,79 +84,101 @@ void PropertyResolver::visit(AST::FunctionDefinition& functionDefinition)
         functionDefinition.block().statements().insert(0, makeUniqueRef<AST::VariableDeclarationsStatement>(Lexer::Token(m_variableDeclarations[0]->origin()), WTFMove(m_variableDeclarations)));
 }
 
-static Optional<UniqueRef<AST::Expression>> setterCall(AST::DotExpression& dotExpression, UniqueRef<AST::Expression>&& newValue, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, const std::function<UniqueRef<AST::Expression>()>& pointerToLeftValueFactory)
+static Optional<UniqueRef<AST::Expression>> setterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, UniqueRef<AST::Expression>&& newValue, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, const std::function<UniqueRef<AST::Expression>()>& pointerToLeftValueFactory, AST::VariableDeclaration* indexVariable)
 {
-    if (dotExpression.anderFunction()) {
+    auto maybeAddIndexArgument = [&](Vector<UniqueRef<AST::Expression>>& arguments) {
+        if (!indexVariable)
+            return;
+        auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*indexVariable));
+        ASSERT(indexVariable->type());
+        variableReference->setType(indexVariable->type()->clone());
+        variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
+        arguments.append(WTFMove(variableReference));
+    };
+
+    if (relevantAnder) {
         // *operator&.foo(&v) = newValue
-        if (!dotExpression.threadAnderFunction())
-            return WTF::nullopt;
-        
         Vector<UniqueRef<AST::Expression>> arguments;
         arguments.append(pointerToLeftValueFactory());
-        auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(dotExpression.origin()), String(dotExpression.threadAnderFunction()->name()), WTFMove(arguments));
-        callExpression->setType(dotExpression.threadAnderFunction()->type().clone());
+        maybeAddIndexArgument(arguments);
+
+        auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(relevantAnder->name()), WTFMove(arguments));
+        callExpression->setType(relevantAnder->type().clone());
         callExpression->setTypeAnnotation(AST::RightValue());
-        callExpression->setFunction(*dotExpression.threadAnderFunction());
+        callExpression->setFunction(*relevantAnder);
 
-        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(dotExpression.origin()), WTFMove(callExpression));
-        dereferenceExpression->setType(downcast<AST::PointerType>(dotExpression.threadAnderFunction()->type()).elementType().clone());
+        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(callExpression));
+        dereferenceExpression->setType(downcast<AST::PointerType>(relevantAnder->type()).elementType().clone());
         dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
 
-        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(dotExpression.origin()), WTFMove(dereferenceExpression), WTFMove(newValue));
-        assignmentExpression->setType(downcast<AST::PointerType>(dotExpression.threadAnderFunction()->type()).elementType().clone());
+        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(dereferenceExpression), WTFMove(newValue));
+        assignmentExpression->setType(downcast<AST::PointerType>(relevantAnder->type()).elementType().clone());
         assignmentExpression->setTypeAnnotation(AST::RightValue());
 
         return UniqueRef<AST::Expression>(WTFMove(assignmentExpression));
     }
 
     // v = operator.foo=(v, newValue)
-    ASSERT(dotExpression.setterFunction());
+    ASSERT(propertyAccessExpression.setterFunction());
 
     Vector<UniqueRef<AST::Expression>> arguments;
     arguments.append(leftValueFactory());
+    maybeAddIndexArgument(arguments);
     arguments.append(WTFMove(newValue));
-    auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(dotExpression.origin()), String(dotExpression.setterFunction()->name()), WTFMove(arguments));
-    callExpression->setType(dotExpression.setterFunction()->type().clone());
+
+    auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(propertyAccessExpression.setterFunction()->name()), WTFMove(arguments));
+    callExpression->setType(propertyAccessExpression.setterFunction()->type().clone());
     callExpression->setTypeAnnotation(AST::RightValue());
-    callExpression->setFunction(*dotExpression.setterFunction());
+    callExpression->setFunction(*propertyAccessExpression.setterFunction());
 
-    auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(dotExpression.origin()), leftValueFactory(), WTFMove(callExpression));
-    assignmentExpression->setType(dotExpression.setterFunction()->type().clone());
+    auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), leftValueFactory(), WTFMove(callExpression));
+    assignmentExpression->setType(propertyAccessExpression.setterFunction()->type().clone());
     assignmentExpression->setTypeAnnotation(AST::RightValue());
 
     return UniqueRef<AST::Expression>(WTFMove(assignmentExpression));
 }
 
-static Optional<UniqueRef<AST::Expression>> getterCall(AST::DotExpression& dotExpression, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, const std::function<UniqueRef<AST::Expression>()>& pointerToLeftValueFactory)
+static Optional<UniqueRef<AST::Expression>> getterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, const std::function<UniqueRef<AST::Expression>()>& pointerToLeftValueFactory, AST::VariableDeclaration* indexVariable)
 {
-    if (dotExpression.anderFunction()) {
+    auto maybeAddIndexArgument = [&](Vector<UniqueRef<AST::Expression>>& arguments) {
+        if (!indexVariable)
+            return;
+        auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*indexVariable));
+        ASSERT(indexVariable->type());
+        variableReference->setType(indexVariable->type()->clone());
+        variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
+        arguments.append(WTFMove(variableReference));
+    };
+
+    if (relevantAnder) {
         // *operator&.foo(&v)
-        if (!dotExpression.threadAnderFunction())
-            return WTF::nullopt;
-        
         Vector<UniqueRef<AST::Expression>> arguments;
         arguments.append(pointerToLeftValueFactory());
-        auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(dotExpression.origin()), String(dotExpression.threadAnderFunction()->name()), WTFMove(arguments));
-        callExpression->setType(dotExpression.threadAnderFunction()->type().clone());
+        maybeAddIndexArgument(arguments);
+
+        auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(relevantAnder->name()), WTFMove(arguments));
+        callExpression->setType(relevantAnder->type().clone());
         callExpression->setTypeAnnotation(AST::RightValue());
-        callExpression->setFunction(*dotExpression.threadAnderFunction());
+        callExpression->setFunction(*relevantAnder);
 
-        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(dotExpression.origin()), WTFMove(callExpression));
-        dereferenceExpression->setType(downcast<AST::PointerType>(dotExpression.threadAnderFunction()->type()).elementType().clone());
+        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(callExpression));
+        dereferenceExpression->setType(downcast<AST::PointerType>(relevantAnder->type()).elementType().clone());
         dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
 
         return UniqueRef<AST::Expression>(WTFMove(dereferenceExpression));
     }
 
     // operator.foo(v)
-    ASSERT(dotExpression.getterFunction());
+    ASSERT(propertyAccessExpression.getterFunction());
     
     Vector<UniqueRef<AST::Expression>> arguments;
     arguments.append(leftValueFactory());
-    auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(dotExpression.origin()), String(dotExpression.getterFunction()->name()), WTFMove(arguments));
-    callExpression->setType(dotExpression.getterFunction()->type().clone());
+    maybeAddIndexArgument(arguments);
+
+    auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(propertyAccessExpression.getterFunction()->name()), WTFMove(arguments));
+    callExpression->setType(propertyAccessExpression.getterFunction()->type().clone());
     callExpression->setTypeAnnotation(AST::RightValue());
-    callExpression->setFunction(*dotExpression.getterFunction());
+    callExpression->setFunction(*propertyAccessExpression.getterFunction());
 
     return UniqueRef<AST::Expression>(WTFMove(callExpression));
 }
@@ -161,7 +192,7 @@ struct ModificationResult {
     Vector<UniqueRef<AST::Expression>> expressions;
     UniqueRef<AST::Expression> result;
 };
-static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::function<Optional<ModificationResult>(Optional<UniqueRef<AST::Expression>>&&)> modification)
+static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAccessExpression, std::function<Optional<ModificationResult>(Optional<UniqueRef<AST::Expression>>&&)> modification)
 {
     // Consider a.b.c.d++;
     // This would get transformed into:
@@ -181,22 +212,22 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
     // r = operator.d=(r, newValue);
     // q = operator.c=(q, r);
     //
-    // Step 4:
+    // Step 5:
     // *p = operator.b=(*p, q);
 
     // If the expression is a.b.c.d = e, Step 3 disappears and "newValue" in step 4 becomes "e".
     
 
     // Find the ".b" ".c" and ".d" expressions. They end up in the order [".d", ".c", ".b"].
-    Vector<std::reference_wrapper<AST::DotExpression>> chain;
-    AST::DotExpression* iterator = &dotExpression;
+    Vector<std::reference_wrapper<AST::PropertyAccessExpression>> chain;
+    AST::PropertyAccessExpression* iterator = &propertyAccessExpression;
     while (true) {
         chain.append(*iterator);
         if (iterator->base().typeAnnotation().leftAddressSpace())
             break;
         ASSERT(!iterator->base().typeAnnotation().isRightValue());
-        ASSERT(is<AST::DotExpression>(iterator->base())); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198163 Make this work with index expressions
-        iterator = &downcast<AST::DotExpression>(iterator->base());
+        ASSERT(is<AST::PropertyAccessExpression>(iterator->base()));
+        iterator = &downcast<AST::PropertyAccessExpression>(iterator->base());
     }
     auto leftExpression = iterator->takeBase();
     AST::Expression& innerLeftExpression = leftExpression;
@@ -208,8 +239,39 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
     Vector<UniqueRef<AST::VariableDeclaration>> intermediateVariables;
     intermediateVariables.reserveInitialCapacity(chain.size() - 1);
     for (size_t i = 1; i < chain.size(); ++i) {
-        auto& dotExpression = static_cast<AST::DotExpression&>(chain[i]);
-        intermediateVariables.uncheckedAppend(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(dotExpression.origin()), AST::Qualifiers(), dotExpression.resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt));
+        auto& propertyAccessExpression = static_cast<AST::PropertyAccessExpression&>(chain[i]);
+        intermediateVariables.uncheckedAppend(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(propertyAccessExpression.origin()), AST::Qualifiers(), propertyAccessExpression.resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt));
+    }
+
+    // Consider a[foo()][b] = c;
+    // Naively, This would get expanded to:
+    //
+    // temp = operator[](a, foo());
+    // temp = operator[]=(temp, b, c);
+    // a = operator[]=(a, foo(), temp);
+    //
+    // However, if we did this, we would have to run foo() twice, which would be incorrect.
+    // Instead, we need to save foo() and b into more temporary variables.
+    // These temporary variables are parallel to "chain" above, with nullopt referring to a DotExpression (which doesn't have an index value to save to a variable).
+    //
+    // Instead, this gets expanded to:
+    //
+    // p = &a;
+    // temp = foo();
+    // q = operator[](*p, temp);
+    // temp2 = b;
+    // q = operator[]=(q, temp2, c);
+    // *p = operator[]=(*p, temp, q);
+
+    Vector<Optional<UniqueRef<AST::VariableDeclaration>>> indexVariables;
+    indexVariables.reserveInitialCapacity(chain.size());
+    for (AST::PropertyAccessExpression& propertyAccessExpression : chain) {
+        if (!is<AST::IndexExpression>(propertyAccessExpression)) {
+            indexVariables.append(WTF::nullopt);
+            continue;
+        }
+        auto& indexExpression = downcast<AST::IndexExpression>(propertyAccessExpression);
+        indexVariables.uncheckedAppend(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(propertyAccessExpression.origin()), AST::Qualifiers(), indexExpression.indexExpression().resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt));
     }
 
     Vector<UniqueRef<AST::Expression>> expressions;
@@ -248,7 +310,7 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
         variableReference->setType(pointerVariable->type()->clone());
         variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
 
-        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(dotExpression.origin()), WTFMove(variableReference));
+        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
         ASSERT(pointerVariable->type());
         dereferenceExpression->setType(downcast<AST::PointerType>(*pointerVariable->type()).elementType().clone());
         dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(*pointerVariable->type()).addressSpace() });
@@ -261,9 +323,9 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
             variableReference->setType(previous->type()->clone());
             variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
 
-            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(dotExpression.origin()), WTFMove(variableReference));
+            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
             ASSERT(previous->type());
-            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(dotExpression.origin()), AST::AddressSpace::Thread, previous->type()->clone()));
+            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), AST::AddressSpace::Thread, previous->type()->clone()));
             makePointerExpression->setTypeAnnotation(AST::RightValue());
             return makePointerExpression;
         }
@@ -274,11 +336,32 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
         variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
         return variableReference;
     };
+    auto appendIndexAssignment = [&](AST::PropertyAccessExpression& propertyAccessExpression, Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable) {
+        if (!indexVariable)
+            return;
+
+        auto& indexExpression = downcast<AST::IndexExpression>(propertyAccessExpression);
+
+        auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*indexVariable));
+        ASSERT(indexVariable->get().type());
+        variableReference->setType(indexVariable->get().type()->clone());
+        variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
+
+        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference), indexExpression.takeIndex());
+        assignmentExpression->setType(indexVariable->get().type()->clone());
+        assignmentExpression->setTypeAnnotation(AST::RightValue());
+
+        expressions.append(WTFMove(assignmentExpression));
+    };
     for (size_t i = chain.size(); --i; ) {
-        AST::DotExpression& dotExpression = chain[i];
+        AST::PropertyAccessExpression& propertyAccessExpression = chain[i];
         AST::VariableDeclaration& variableDeclaration = intermediateVariables[i - 1];
+        Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable = indexVariables[i];
+
+        appendIndexAssignment(propertyAccessExpression, indexVariable);
 
-        auto callExpression = getterCall(dotExpression, previousLeftValue, pointerToPreviousLeftValue);
+        AST::FunctionDeclaration* relevantAnder = i == chain.size() - 1 ? propertyAccessExpression.anderFunction() : propertyAccessExpression.threadAnderFunction();
+        auto callExpression = getterCall(propertyAccessExpression, relevantAnder, previousLeftValue, pointerToPreviousLeftValue, indexVariable ? &*indexVariable : nullptr);
 
         if (!callExpression)
             return WTF::nullopt;
@@ -288,7 +371,7 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
         variableReference->setType(variableDeclaration.type()->clone());
         variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
 
-        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(dotExpression.origin()), WTFMove(variableReference), WTFMove(*callExpression));
+        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference), WTFMove(*callExpression));
         assignmentExpression->setType(variableDeclaration.type()->clone());
         assignmentExpression->setTypeAnnotation(AST::RightValue());
 
@@ -296,7 +379,9 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
         
         previous = &variableDeclaration;
     }
-    auto lastGetterCallExpression = getterCall(chain[0], previousLeftValue, pointerToPreviousLeftValue);
+    appendIndexAssignment(chain[0], indexVariables[0]);
+    AST::FunctionDeclaration* relevantAnder = chain.size() == 1 ? propertyAccessExpression.anderFunction() : propertyAccessExpression.threadAnderFunction();
+    auto lastGetterCallExpression = getterCall(chain[0], relevantAnder, previousLeftValue, pointerToPreviousLeftValue, indexVariables[0] ? &*(indexVariables[0]) : nullptr);
 
     // Step 3:
     auto modificationResult = modification(WTFMove(lastGetterCallExpression));
@@ -309,10 +394,11 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
     UniqueRef<AST::Expression> rightValue = WTFMove(modificationResult->result);
     auto expressionType = rightValue->resolvedType().clone();
     for (size_t i = 0; i < chain.size() - 1; ++i) {
-        AST::DotExpression& dotExpression = chain[i];
+        AST::PropertyAccessExpression& propertyAccessExpression = chain[i];
         AST::VariableDeclaration& variableDeclaration = intermediateVariables[i];
+        Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable = indexVariables[i];
 
-        auto assignmentExpression = setterCall(dotExpression, WTFMove(rightValue), [&]() -> UniqueRef<AST::Expression> {
+        auto assignmentExpression = setterCall(propertyAccessExpression, propertyAccessExpression.threadAnderFunction(), WTFMove(rightValue), [&]() -> UniqueRef<AST::Expression> {
             auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
             ASSERT(variableDeclaration.type());
             variableReference->setType(variableDeclaration.type()->clone());
@@ -324,12 +410,12 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
             variableReference->setType(variableDeclaration.type()->clone());
             variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
 
-            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(dotExpression.origin()), WTFMove(variableReference));
+            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
             ASSERT(variableDeclaration.type());
-            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(dotExpression.origin()), AST::AddressSpace::Thread, variableDeclaration.type()->clone()));
+            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), AST::AddressSpace::Thread, variableDeclaration.type()->clone()));
             makePointerExpression->setTypeAnnotation(AST::RightValue());
             return makePointerExpression;
-        });
+        }, indexVariable ? &*indexVariable : nullptr);
 
         if (!assignmentExpression)
             return WTF::nullopt;
@@ -344,13 +430,14 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
 
     // Step 5:
     {
-        auto assignmentExpression = setterCall(chain[chain.size() - 1], WTFMove(rightValue), [&]() -> UniqueRef<AST::Expression> {
+        AST::PropertyAccessExpression& propertyAccessExpression = chain[chain.size() - 1];
+        auto assignmentExpression = setterCall(propertyAccessExpression, propertyAccessExpression.anderFunction(), WTFMove(rightValue), [&]() -> UniqueRef<AST::Expression> {
             auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
             ASSERT(pointerVariable->type());
             variableReference->setType(pointerVariable->type()->clone());
             variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
 
-            auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(dotExpression.origin()), WTFMove(variableReference));
+            auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
             ASSERT(pointerVariable->type());
             dereferenceExpression->setType(downcast<AST::PointerType>(*pointerVariable->type()).elementType().clone());
             dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(*pointerVariable->type()).addressSpace() });
@@ -361,7 +448,7 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
             variableReference->setType(pointerVariable->type()->clone());
             variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
             return variableReference;
-        });
+        }, indexVariables[indexVariables.size() - 1] ? &*(indexVariables[indexVariables.size() - 1]) : nullptr);
 
         if (!assignmentExpression)
             return WTF::nullopt;
@@ -373,6 +460,10 @@ static Optional<ModifyResult> modify(AST::DotExpression& dotExpression, std::fun
     variableDeclarations.append(WTFMove(pointerVariable));
     for (auto& intermediateVariable : intermediateVariables)
         variableDeclarations.append(WTFMove(intermediateVariable));
+    for (auto& indexVariable : indexVariables) {
+        if (indexVariable)
+            variableDeclarations.append(WTFMove(*indexVariable));
+    }
 
     return {{ innerLeftExpression, WTFMove(expressions), WTFMove(variableDeclarations) }};
 }
@@ -385,16 +476,13 @@ void PropertyResolver::visit(AST::AssignmentExpression& assignmentExpression)
         return;
     }
     ASSERT(!assignmentExpression.left().typeAnnotation().isRightValue());
-    if (!is<AST::DotExpression>(assignmentExpression.left())) {
-        setError(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198163 Make this work with index expressions.
-        return;
-    }
+    ASSERT(is<AST::PropertyAccessExpression>(assignmentExpression.left()));
 
     auto type = assignmentExpression.right().resolvedType().clone();
 
     checkErrorAndVisit(assignmentExpression.right());
 
-    auto modifyResult = modify(downcast<AST::DotExpression>(assignmentExpression.left()), [&](Optional<UniqueRef<AST::Expression>>&&) -> Optional<ModificationResult> {
+    auto modifyResult = modify(downcast<AST::PropertyAccessExpression>(assignmentExpression.left()), [&](Optional<UniqueRef<AST::Expression>>&&) -> Optional<ModificationResult> {
         return {{ Vector<UniqueRef<AST::Expression>>(), assignmentExpression.takeRight() }};
     });
 
@@ -424,6 +512,8 @@ void PropertyResolver::visit(AST::ReadModifyWriteExpression& readModifyWriteExpr
         // newValue = ...;
         // *p = newValue;
 
+        simplifyLeftValue(readModifyWriteExpression.leftValue());
+
         auto baseType = readModifyWriteExpression.leftValue().resolvedType().clone();
         auto pointerType = makeUniqueRef<AST::PointerType>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), *readModifyWriteExpression.leftValue().typeAnnotation().leftAddressSpace(), baseType->clone());
 
@@ -520,11 +610,11 @@ void PropertyResolver::visit(AST::ReadModifyWriteExpression& readModifyWriteExpr
     }
 
     ASSERT(!readModifyWriteExpression.leftValue().typeAnnotation().isRightValue());
-    if (!is<AST::DotExpression>(readModifyWriteExpression.leftValue())) {
-        setError(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198163 Make this work with index expressions.
+    if (!is<AST::PropertyAccessExpression>(readModifyWriteExpression.leftValue())) {
+        setError();
         return;
     }
-    auto modifyResult = modify(downcast<AST::DotExpression>(readModifyWriteExpression.leftValue()), [&](Optional<UniqueRef<AST::Expression>>&& lastGetterCallExpression) -> Optional<ModificationResult> {
+    auto modifyResult = modify(downcast<AST::PropertyAccessExpression>(readModifyWriteExpression.leftValue()), [&](Optional<UniqueRef<AST::Expression>>&& lastGetterCallExpression) -> Optional<ModificationResult> {
         Vector<UniqueRef<AST::Expression>> expressions;
         if (!lastGetterCallExpression)
             return WTF::nullopt;
@@ -581,128 +671,150 @@ void PropertyResolver::visit(AST::ReadModifyWriteExpression& readModifyWriteExpr
     m_variableDeclarations.append(WTFMove(newVariableDeclaration));
 }
 
-bool PropertyResolver::simplifyRightValue(AST::DotExpression& dotExpression)
+bool PropertyResolver::simplifyRightValue(AST::PropertyAccessExpression& propertyAccessExpression)
 {
-    Lexer::Token origin = dotExpression.origin();
+    Lexer::Token origin = propertyAccessExpression.origin();
 
-    checkErrorAndVisit(dotExpression.base());
+    checkErrorAndVisit(propertyAccessExpression.base());
 
-    if (auto* anderFunction = dotExpression.anderFunction()) {
-        auto& base = dotExpression.base();
-        if (auto leftAddressSpace = base.typeAnnotation().leftAddressSpace()) {
-            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), dotExpression.takeBase());
+    auto& base = propertyAccessExpression.base();
+    if (auto leftAddressSpace = base.typeAnnotation().leftAddressSpace()) {
+        if (auto* anderFunction = propertyAccessExpression.anderFunction()) {
+            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), propertyAccessExpression.takeBase());
             makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), *leftAddressSpace, base.resolvedType().clone()));
             makePointerExpression->setTypeAnnotation(AST::RightValue());
 
             Vector<UniqueRef<AST::Expression>> arguments;
             arguments.append(WTFMove(makePointerExpression));
+            if (is<AST::IndexExpression>(propertyAccessExpression))
+                arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
             auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
             callExpression->setType(anderFunction->type().clone());
             callExpression->setTypeAnnotation(AST::RightValue());
             callExpression->setFunction(*anderFunction);
 
-            auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(dotExpression, WTFMove(origin), WTFMove(callExpression));
+            auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(callExpression));
             dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
             dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(anderFunction->type()).addressSpace() });
             return true;
         }
+    }
 
-        // We have an ander, but no left value to call it on. Let's save the value into a temporary variable to create a left value.
-        // This is effectively inlining the functions the spec says are generated.
-        if (!dotExpression.threadAnderFunction())
-            return false;
+    if (propertyAccessExpression.getterFunction()) {
+        auto& getterFunction = *propertyAccessExpression.getterFunction();
+        Vector<UniqueRef<AST::Expression>> arguments;
+        arguments.append(propertyAccessExpression.takeBase());
+        if (is<AST::IndexExpression>(propertyAccessExpression))
+            arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
+        auto* callExpression = AST::replaceWith<AST::CallExpression>(propertyAccessExpression, WTFMove(origin), String(getterFunction.name()), WTFMove(arguments));
+        callExpression->setFunction(getterFunction);
+        callExpression->setType(getterFunction.type().clone());
+        callExpression->setTypeAnnotation(AST::RightValue());
+        return true;
+    }
 
-        auto variableDeclaration = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), base.resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt);
+    // We have an ander, but no left value to call it on. Let's save the value into a temporary variable to create a left value.
+    // This is effectively inlining the functions the spec says are generated.
+    ASSERT(propertyAccessExpression.threadAnderFunction());
+    auto* threadAnderFunction = propertyAccessExpression.threadAnderFunction();
 
-        auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
-        variableReference1->setType(base.resolvedType().clone());
-        variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+    auto variableDeclaration = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), base.resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt);
 
-        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(origin), WTFMove(variableReference1), dotExpression.takeBase());
-        assignmentExpression->setType(base.resolvedType().clone());
-        assignmentExpression->setTypeAnnotation(AST::RightValue());
+    auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
+    variableReference1->setType(base.resolvedType().clone());
+    variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
 
-        auto variableReference2 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
-        variableReference2->setType(base.resolvedType().clone());
-        variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+    auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(origin), WTFMove(variableReference1), propertyAccessExpression.takeBase());
+    assignmentExpression->setType(base.resolvedType().clone());
+    assignmentExpression->setTypeAnnotation(AST::RightValue());
 
-        auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), WTFMove(variableReference2));
-        makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), AST::AddressSpace::Thread, base.resolvedType().clone()));
-        makePointerExpression->setTypeAnnotation(AST::RightValue());
+    auto variableReference2 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
+    variableReference2->setType(base.resolvedType().clone());
+    variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
 
-        Vector<UniqueRef<AST::Expression>> arguments;
-        arguments.append(WTFMove(makePointerExpression));
-        auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
-        callExpression->setType(anderFunction->type().clone());
-        callExpression->setTypeAnnotation(AST::RightValue());
-        callExpression->setFunction(*anderFunction);
+    auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), WTFMove(variableReference2));
+    makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), AST::AddressSpace::Thread, base.resolvedType().clone()));
+    makePointerExpression->setTypeAnnotation(AST::RightValue());
 
-        auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(WTFMove(origin), WTFMove(callExpression));
-        dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
-        dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+    Vector<UniqueRef<AST::Expression>> arguments;
+    arguments.append(WTFMove(makePointerExpression));
+    if (is<AST::IndexExpression>(propertyAccessExpression))
+        arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
+    auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(threadAnderFunction->name()), WTFMove(arguments));
+    callExpression->setType(threadAnderFunction->type().clone());
+    callExpression->setTypeAnnotation(AST::RightValue());
+    callExpression->setFunction(*threadAnderFunction);
 
-        Vector<UniqueRef<AST::Expression>> expressions;
-        expressions.append(WTFMove(assignmentExpression));
-        expressions.append(WTFMove(dereferenceExpression));
-        auto* commaExpression = AST::replaceWith<AST::CommaExpression>(dotExpression, WTFMove(origin), WTFMove(expressions));
-        commaExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
-        commaExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+    auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(WTFMove(origin), WTFMove(callExpression));
+    dereferenceExpression->setType(downcast<AST::PointerType>(threadAnderFunction->type()).elementType().clone());
+    dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
 
-        m_variableDeclarations.append(WTFMove(variableDeclaration));
-        return true;
-    }
+    Vector<UniqueRef<AST::Expression>> expressions;
+    expressions.append(WTFMove(assignmentExpression));
+    expressions.append(WTFMove(dereferenceExpression));
+    auto* commaExpression = AST::replaceWith<AST::CommaExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(expressions));
+    commaExpression->setType(downcast<AST::PointerType>(threadAnderFunction->type()).elementType().clone());
+    commaExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
 
-    ASSERT(dotExpression.getterFunction());
-    auto& getterFunction = *dotExpression.getterFunction();
-    Vector<UniqueRef<AST::Expression>> arguments;
-    arguments.append(dotExpression.takeBase());
-    auto* callExpression = AST::replaceWith<AST::CallExpression>(dotExpression, WTFMove(origin), String(getterFunction.name()), WTFMove(arguments));
-    callExpression->setFunction(getterFunction);
-    callExpression->setType(getterFunction.type().clone());
-    callExpression->setTypeAnnotation(AST::RightValue());
+    m_variableDeclarations.append(WTFMove(variableDeclaration));
     return true;
+
 }
 
 class LeftValueSimplifier : public Visitor {
-public:
+private:
     void visit(AST::DotExpression&) override;
+    void visit(AST::IndexExpression&) override;
     void visit(AST::DereferenceExpression&) override;
 
-private:
+    void finishVisiting(AST::PropertyAccessExpression&);
 };
 
-void LeftValueSimplifier::visit(AST::DotExpression& dotExpression)
+void LeftValueSimplifier::finishVisiting(AST::PropertyAccessExpression& propertyAccessExpression)
 {
-    Visitor::visit(dotExpression);
-    ASSERT(dotExpression.base().typeAnnotation().leftAddressSpace());
-    ASSERT(dotExpression.anderFunction());
-
-    Lexer::Token origin = dotExpression.origin();
-    auto* anderFunction = dotExpression.anderFunction();
-    auto& base = dotExpression.base();
-    auto leftAddressSpace = *dotExpression.base().typeAnnotation().leftAddressSpace();
-    auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), dotExpression.takeBase());
+    ASSERT(propertyAccessExpression.base().typeAnnotation().leftAddressSpace());
+    ASSERT(propertyAccessExpression.anderFunction());
+
+    Lexer::Token origin = propertyAccessExpression.origin();
+    auto* anderFunction = propertyAccessExpression.anderFunction();
+    auto& base = propertyAccessExpression.base();
+    auto leftAddressSpace = *propertyAccessExpression.base().typeAnnotation().leftAddressSpace();
+    auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), propertyAccessExpression.takeBase());
     makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), leftAddressSpace, base.resolvedType().clone()));
     makePointerExpression->setTypeAnnotation(AST::RightValue());
 
     Vector<UniqueRef<AST::Expression>> arguments;
     arguments.append(WTFMove(makePointerExpression));
+    if (is<AST::IndexExpression>(propertyAccessExpression))
+        arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
     auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
     callExpression->setType(anderFunction->type().clone());
     callExpression->setTypeAnnotation(AST::RightValue());
     callExpression->setFunction(*anderFunction);
 
-    auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(dotExpression, WTFMove(origin), WTFMove(callExpression));
+    auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(callExpression));
     dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
     dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(anderFunction->type()).addressSpace() });
 }
 
+void LeftValueSimplifier::visit(AST::DotExpression& dotExpression)
+{
+    Visitor::visit(dotExpression);
+    finishVisiting(dotExpression);
+}
+
+void LeftValueSimplifier::visit(AST::IndexExpression& indexExpression)
+{
+    Visitor::visit(indexExpression);
+    PropertyResolver().Visitor::visit(indexExpression.indexExpression());
+    finishVisiting(indexExpression);
+}
+
 void LeftValueSimplifier::visit(AST::DereferenceExpression& dereferenceExpression)
 {
     // Dereference expressions are the only expressions where the children might be more-right than we are.
     // For example, a dereference expression may be a left value but its child may be a call expression which is a right value.
     // LeftValueSimplifier doesn't handle right values, so we instead need to use PropertyResolver.
-    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198170 What about function call arguments?
     PropertyResolver().Visitor::visit(dereferenceExpression);
 }
 
index 9663067..1c4527a 100644 (file)
@@ -375,6 +375,63 @@ native typedef TextureDepth2DArray<float>;
 native typedef RWTextureDepth2DArray<float>;
 native typedef TextureDepthCube<float>;
 
+native operator uchar(ushort);
+native operator uchar(uint);
+native operator uchar(char);
+native operator uchar(short);
+native operator uchar(int);
+native operator uchar(half);
+native operator uchar(float);
+native operator ushort(uchar);
+native operator ushort(uint);
+native operator ushort(char);
+native operator ushort(short);
+native operator ushort(int);
+native operator ushort(half);
+native operator ushort(float);
+native operator uint(uchar);
+native operator uint(ushort);
+native operator uint(char);
+native operator uint(short);
+native operator uint(int);
+native operator uint(half);
+native operator uint(float);
+native operator char(uchar);
+native operator char(ushort);
+native operator char(uint);
+native operator char(short);
+native operator char(int);
+native operator char(half);
+native operator char(float);
+native operator short(uchar);
+native operator short(ushort);
+native operator short(uint);
+native operator short(char);
+native operator short(int);
+native operator short(half);
+native operator short(float);
+native operator int(uchar);
+native operator int(ushort);
+native operator int(uint);
+native operator int(char);
+native operator int(short);
+native operator int(half);
+native operator int(float);
+native operator half(uchar);
+native operator half(ushort);
+native operator half(uint);
+native operator half(char);
+native operator half(short);
+native operator half(int);
+native operator half(float);
+native operator float(uchar);
+native operator float(ushort);
+native operator float(uint);
+native operator float(char);
+native operator float(short);
+native operator float(int);
+native operator float(half);
+
 native float operator.x(float4);
 native float operator.y(float4);
 native float operator.z(float4);
index 945d0ce..3c0bc2b 100644 (file)
@@ -44,28 +44,6 @@ bool synthesizeStructureAccessors(Program& program)
     bool isOperator = true;
     for (auto& structureDefinition : program.structureDefinitions()) {
         for (auto& structureElement : structureDefinition->structureElements()) {
-            {
-                // The getter: operator.field
-                auto variableDeclaration = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(structureElement.origin()), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(AST::TypeReference::wrap(Lexer::Token(structureElement.origin()), structureDefinition)), String(), WTF::nullopt, WTF::nullopt);
-                AST::VariableDeclarations parameters;
-                parameters.append(WTFMove(variableDeclaration));
-                AST::NativeFunctionDeclaration nativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(structureElement.origin()), AST::AttributeBlock(), WTF::nullopt, structureElement.type().clone(), makeString("operator.", structureElement.name()), WTFMove(parameters), WTF::nullopt, isOperator));
-                if (!program.append(WTFMove(nativeFunctionDeclaration)))
-                    return false;
-            }
-
-            {
-                // The setter: operator.field=
-                auto variableDeclaration1 = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(structureElement.origin()), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(AST::TypeReference::wrap(Lexer::Token(structureElement.origin()), structureDefinition)), String(), WTF::nullopt, WTF::nullopt);
-                auto variableDeclaration2 = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(structureElement.origin()), AST::Qualifiers(), structureElement.type().clone(), String(), WTF::nullopt, WTF::nullopt);
-                AST::VariableDeclarations parameters;
-                parameters.append(WTFMove(variableDeclaration1));
-                parameters.append(WTFMove(variableDeclaration2));
-                AST::NativeFunctionDeclaration nativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(structureElement.origin()), AST::AttributeBlock(), WTF::nullopt, AST::TypeReference::wrap(Lexer::Token(structureElement.origin()), structureDefinition), makeString("operator.", structureElement.name(), '='), WTFMove(parameters), WTF::nullopt, isOperator));
-                if (!program.append(WTFMove(nativeFunctionDeclaration)))
-                    return false;
-            }
-
             // The ander: operator&.field
             auto createAnder = [&](AST::AddressSpace addressSpace) -> AST::NativeFunctionDeclaration {
                 auto argumentType = makeUniqueRef<AST::PointerType>(Lexer::Token(structureElement.origin()), addressSpace, AST::TypeReference::wrap(Lexer::Token(structureElement.origin()), structureDefinition));