WSL should support ++, --, +=, and all of those things
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 15 Sep 2017 20:36:27 +0000 (20:36 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 15 Sep 2017 20:36:27 +0000 (20:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176975

Reviewed by Myles Maxfield.

This adds an internal AST construct called LetExpression, that allows us to anonymously capture
a value. This change uses LetExpression to implement prefix and suffix ++/-- and all of the
+=/-=/etc.

* WebGPUShadingLanguageRI/All.js:
* WebGPUShadingLanguageRI/Checker.js:
* WebGPUShadingLanguageRI/EBufferBuilder.js:
(EBufferBuilder.prototype.visitVariableDecl):
(EBufferBuilder.prototype.visitLetExpression):
(EBufferBuilder):
* WebGPUShadingLanguageRI/Evaluator.js:
(Evaluator.prototype.visitLetExpression):
* WebGPUShadingLanguageRI/LetExpression.js: Added.
(LetExpression):
(LetExpression.prototype.get origin):
(LetExpression.prototype.get name):
(LetExpression.prototype.toString):
* WebGPUShadingLanguageRI/Lexer.js:
(Lexer.prototype.next):
(Lexer):
* WebGPUShadingLanguageRI/NameResolver.js:
(NameResolver.prototype.visitProtocolDecl):
* WebGPUShadingLanguageRI/Parse.js:
(isCallExpression):
(finishParsingPostIncrement):
(parsePossibleSuffix):
(finishParsingPreIncrement):
(parsePreIncrement):
(parsePossiblePrefix):
(parsePossibleAssignment):
(parsePostIncrement):
(parseEffectfulExpression):
* WebGPUShadingLanguageRI/Rewriter.js:
(Rewriter.prototype.visitFunctionLikeBlock):
(Rewriter.prototype.visitLetExpression):
(Rewriter):
* WebGPUShadingLanguageRI/StandardLibrary.js:
(int.operator):
(uint.operator):
* WebGPUShadingLanguageRI/Test.html:
* WebGPUShadingLanguageRI/Test.js:
(TEST_prefixPlusPlus):
(TEST_prefixPlusPlusResult):
(TEST_postfixPlusPlus):
(TEST_postfixPlusPlusResult):
(TEST_prefixMinusMinus):
(TEST_prefixMinusMinusResult):
(TEST_postfixMinusMinus):
(TEST_postfixMinusMinusResult):
(TEST_plusEquals):
(TEST_plusEqualsResult):
(TEST_minusEquals):
(TEST_minusEqualsResult):
(TEST_timesEquals):
(TEST_timesEqualsResult):
(TEST_divideEquals):
(TEST_divideEqualsResult):
* WebGPUShadingLanguageRI/Visitor.js:
(Visitor.prototype.visitProtocolDecl):

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

14 files changed:
Tools/ChangeLog
Tools/WebGPUShadingLanguageRI/All.js
Tools/WebGPUShadingLanguageRI/Checker.js
Tools/WebGPUShadingLanguageRI/EBufferBuilder.js
Tools/WebGPUShadingLanguageRI/Evaluator.js
Tools/WebGPUShadingLanguageRI/LetExpression.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/Lexer.js
Tools/WebGPUShadingLanguageRI/NameResolver.js
Tools/WebGPUShadingLanguageRI/Parse.js
Tools/WebGPUShadingLanguageRI/Rewriter.js
Tools/WebGPUShadingLanguageRI/StandardLibrary.js
Tools/WebGPUShadingLanguageRI/Test.html
Tools/WebGPUShadingLanguageRI/Test.js
Tools/WebGPUShadingLanguageRI/Visitor.js

index 87208c6..9886386 100644 (file)
@@ -1,3 +1,70 @@
+2017-09-14  Filip Pizlo  <fpizlo@apple.com>
+
+        WSL should support ++, --, +=, and all of those things
+        https://bugs.webkit.org/show_bug.cgi?id=176975
+
+        Reviewed by Myles Maxfield.
+        
+        This adds an internal AST construct called LetExpression, that allows us to anonymously capture
+        a value. This change uses LetExpression to implement prefix and suffix ++/-- and all of the
+        +=/-=/etc.
+
+        * WebGPUShadingLanguageRI/All.js:
+        * WebGPUShadingLanguageRI/Checker.js:
+        * WebGPUShadingLanguageRI/EBufferBuilder.js:
+        (EBufferBuilder.prototype.visitVariableDecl):
+        (EBufferBuilder.prototype.visitLetExpression):
+        (EBufferBuilder):
+        * WebGPUShadingLanguageRI/Evaluator.js:
+        (Evaluator.prototype.visitLetExpression):
+        * WebGPUShadingLanguageRI/LetExpression.js: Added.
+        (LetExpression):
+        (LetExpression.prototype.get origin):
+        (LetExpression.prototype.get name):
+        (LetExpression.prototype.toString):
+        * WebGPUShadingLanguageRI/Lexer.js:
+        (Lexer.prototype.next):
+        (Lexer):
+        * WebGPUShadingLanguageRI/NameResolver.js:
+        (NameResolver.prototype.visitProtocolDecl):
+        * WebGPUShadingLanguageRI/Parse.js:
+        (isCallExpression):
+        (finishParsingPostIncrement):
+        (parsePossibleSuffix):
+        (finishParsingPreIncrement):
+        (parsePreIncrement):
+        (parsePossiblePrefix):
+        (parsePossibleAssignment):
+        (parsePostIncrement):
+        (parseEffectfulExpression):
+        * WebGPUShadingLanguageRI/Rewriter.js:
+        (Rewriter.prototype.visitFunctionLikeBlock):
+        (Rewriter.prototype.visitLetExpression):
+        (Rewriter):
+        * WebGPUShadingLanguageRI/StandardLibrary.js:
+        (int.operator):
+        (uint.operator):
+        * WebGPUShadingLanguageRI/Test.html:
+        * WebGPUShadingLanguageRI/Test.js:
+        (TEST_prefixPlusPlus):
+        (TEST_prefixPlusPlusResult):
+        (TEST_postfixPlusPlus):
+        (TEST_postfixPlusPlusResult):
+        (TEST_prefixMinusMinus):
+        (TEST_prefixMinusMinusResult):
+        (TEST_postfixMinusMinus):
+        (TEST_postfixMinusMinusResult):
+        (TEST_plusEquals):
+        (TEST_plusEqualsResult):
+        (TEST_minusEquals):
+        (TEST_minusEqualsResult):
+        (TEST_timesEquals):
+        (TEST_timesEqualsResult):
+        (TEST_divideEquals):
+        (TEST_divideEqualsResult):
+        * WebGPUShadingLanguageRI/Visitor.js:
+        (Visitor.prototype.visitProtocolDecl):
+
 2017-09-15  Brent Fulgham  <bfulgham@apple.com>
 
         Provide mechanism to immediately end tests
index 7c95bc5..2b7a976 100644 (file)
@@ -83,6 +83,7 @@ load("InstantiateImmediates.js");
 load("IntLiteral.js");
 load("IntLiteralType.js");
 load("Intrinsics.js");
+load("LetExpression.js");
 load("Lexer.js");
 load("LexerToken.js");
 load("LiteralTypeChecker.js");
index bda9aa5..6f25d0d 100644 (file)
@@ -177,8 +177,18 @@ class Checker extends Visitor {
         return field.type;
     }
     
+    visitLetExpression(node)
+    {
+        node.type = node.argument.visit(this);
+        if (!node.type)
+            throw new Error("Did not get type for node: " + node.argument);
+        return node.body.visit(this);
+    }
+    
     visitVariableRef(node)
     {
+        if (!node.variable.type)
+            throw new Error("Variable has no type: " + node.variable);
         return node.variable.type;
     }
     
index cb9eb0c..bb68763 100644 (file)
@@ -55,6 +55,15 @@ class EBufferBuilder extends Visitor {
     visitVariableDecl(node)
     {
         this._createEPtrForNode(node);
+        if (node.initializer)
+            node.initializer.visit(this);
+    }
+    
+    visitLetExpression(node)
+    {
+        this._createEPtrForNode(node);
+        node.argument.visit(this);
+        node.body.visit(this);
     }
 }
 
index 998f670..933eb42 100644 (file)
@@ -212,6 +212,12 @@ class Evaluator extends Visitor {
         throw node;
     }
     
+    visitLetExpression(node)
+    {
+        node.ePtr.copyFrom(node.argument.visit(this), node.type.size);
+        return node.body.visit(this);
+    }
+    
     visitCallExpression(node)
     {
         // We evaluate inlined ASTs, so this can only be a native call.
diff --git a/Tools/WebGPUShadingLanguageRI/LetExpression.js b/Tools/WebGPUShadingLanguageRI/LetExpression.js
new file mode 100644 (file)
index 0000000..ddd83ca
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+let letExpressionCount = 0;
+
+class LetExpression extends Value {
+    constructor(origin)
+    {
+        super();
+        this._origin = origin;
+        this.index = letExpressionCount++;
+        this.argument = null;
+        this.body = null;
+    }
+    
+    get origin() { return this._origin; }
+    get name() { return "let<" + this.index + ">"; }
+    
+    toString()
+    {
+        return this.name + "(" + this.argument + ", " + this.body + ")";
+    }
+}
+
index 3604375..ef2d7e0 100644 (file)
@@ -123,7 +123,7 @@ class Lexer {
         if (/^([0-9]*\.[0-9]+)|([0-9]+\.[0-9]*)/.test(relevantText))
             return result("doubleLiteral");
         
-        if (/^->|>=|<=|==|!=|\+=|-=|\*=|\/=|%=|^=|\|=|&=|([{}()\[\]?:=+*\/,.%!~^&|<>@;-])/.test(relevantText))
+        if (/^->|>=|<=|==|!=|\+=|-=|\*=|\/=|%=|^=|\|=|&=|\+\+|--|([{}()\[\]?:=+*\/,.%!~^&|<>@;-])/.test(relevantText))
             return result("punctuation");
         
         let remaining = relevantText.substring(0, 20).split(/\s/)[0];
index c070030..be504ea 100644 (file)
@@ -202,12 +202,15 @@ class NameResolver extends Visitor {
     {
         this._resolveTypeArguments(node.typeArguments);
         
-        let type = this._nameContext.get(Type, node.name);
-        if (!type)
-            throw new WTypeError(node.origin.originString, "Could not find type named " + node.name);
-        if (!this._nameContext.isDefined(type))
-            throw new WTypeError(node.origin.originString, "Illegal forward use of type named " + node.name);
-        node.type = type;
+        let type = node.type;
+        if (!type) {
+            type = this._nameContext.get(Type, node.name);
+            if (!type)
+                throw new WTypeError(node.origin.originString, "Could not find type named " + node.name);
+            if (!this._nameContext.isDefined(type))
+                throw new WTypeError(node.origin.originString, "Illegal forward use of type named " + node.name);
+            node.type = type;
+        }
         
         if (type.typeParameters.length != node.typeArguments.length)
             throw new WTypeError(node.origin.originString, "Wrong number of type arguments (passed " + node.typeArguments.length + ", expected " + type.typeParameters.length + ")");
@@ -241,6 +244,8 @@ class NameResolver extends Visitor {
     
     visitVariableRef(node)
     {
+        if (node.variable)
+            return;
         let result = this._nameContext.get(Value, node.name);
         if (!result)
             throw new WTypeError(node.origin.originString, "Could not find variable named " + node.name);
@@ -263,7 +268,7 @@ class NameResolver extends Visitor {
         else {
             let type = this._nameContext.get(Type, node.name);
             if (!type)
-                throw new WTypeError(node.origin.originString, "Cannot find any function or type named " + node.name);
+                throw new WTypeError(node.origin.originString, "Cannot find any function or type named \"" + node.name + "\"");
             node.becomeCast(type);
             node.possibleOverloads = this._nameContext.get(Func, "operator cast");
             if (!node.possibleOverloads)
index bb5d2cf..6ed00ad 100644 (file)
@@ -328,16 +328,54 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         return new CallExpression(name, name.text, typeArguments, argumentList);
     }
     
-    function parsePossibleSuffix()
+    function isCallExpression()
     {
-        // First check if this is a call expression.
-        let isCallExpression = lexer.testScope(() => {
+        return lexer.testScope(() => {
             consumeKind("identifier");
             parseTypeArguments();
             consume("(");
         });
+    }
+    
+    function emitIncrement(token, ptr, extraArg)
+    {
+        let args = [new DereferenceExpression(token, VariableRef.wrap(ptr))];
+        if (extraArg)
+            args.push(extraArg);
+        
+        let name = "operator" + token.text;
+        if (/=$/.test(name))
+            name = RegExp.leftContext;
+        
+        if (name == "operator")
+            throw new Error("Invalid name: " + name);
+        
+        return new Assignment(
+            token,
+            new DereferenceExpression(token, VariableRef.wrap(ptr)),
+            new CallExpression(token, name, [], args));
+    }
+    
+    function finishParsingPostIncrement(token, left)
+    {
+        let ptr = new LetExpression(token);
+        ptr.argument = new MakePtrExpression(token, left);
+        
+        let oldValue = new LetExpression(token);
+        oldValue.argument = new DereferenceExpression(token, VariableRef.wrap(ptr));
         
-        if (isCallExpression)
+        ptr.body = oldValue;
+        
+        oldValue.body = new CommaExpression(token, [
+            emitIncrement(token, ptr),
+            VariableRef.wrap(oldValue)
+        ]);
+        return ptr;
+    }
+    
+    function parsePossibleSuffix()
+    {
+        if (isCallExpression())
             return parseCallExpression();
         
         let left = parseTerm();
@@ -346,7 +384,7 @@ function parse(program, origin, originKind, lineNumberOffset, text)
             switch (token.text) {
             case "++":
             case "--":
-                left = new SuffixCallAssignment(token, "operator" + token.text, left);
+                left = finishParsingPostIncrement(token, left);
                 break;
             case ".":
             case "->":
@@ -369,11 +407,31 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         return left;
     }
     
+    function finishParsingPreIncrement(token, left, extraArg)
+    {
+        let ptr = new LetExpression(token);
+        ptr.argument = new MakePtrExpression(token, left);
+        ptr.body = new CommaExpression(token, [
+            emitIncrement(token, ptr, extraArg),
+            new DereferenceExpression(token, VariableRef.wrap(ptr))
+        ]);
+        return ptr;
+    }
+    
+    function parsePreIncrement()
+    {
+        let token = consume("++", "--");
+        let left = parsePossiblePrefix();
+        return finishParsingPreIncrement(token, left);
+    }
+    
     function parsePossiblePrefix()
     {
         let token;
-        if (token = tryConsume("++", "--", "+", "-", "~"))
-            return new CallAssignment(token, "operator" + token.text, parsePossiblePrefix());
+        if (test("++", "--"))
+            return parsePreIncrement();
+        if (token = tryConsume("+", "-", "~"))
+            return new CallExpression(token, "operator" + token.text, [], [parsePossiblePrefix()]);
         if (token = tryConsume("^"))
             return new DereferenceExpression(token, parsePossiblePrefix());
         if (token = tryConsume("&"))
@@ -471,8 +529,7 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         }
         if (operator.text == "=")
             return new Assignment(operator, lhs, parsePossibleAssignment());
-        let name = "operator" + operator.text.substring(0, operator.text.length - 1);
-        return new CallAssignment(operator, name, lhs, parsePossibleAssignment());
+        return finishParsingPreIncrement(operator, lhs, parsePossibleAssignment());
     }
     
     function parseAssignment()
@@ -480,12 +537,24 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         return parsePossibleAssignment("required");
     }
     
+    function parsePostIncrement()
+    {
+        let left = parseTerm();
+        let token = consume("++", "--");
+        return finishParsingPostIncrement(token, left);
+    }
+    
     function parseEffectfulExpression()
     {
-        let assignment = lexer.backtrackingScope(parseAssignment);
-        if (assignment)
-            return assignment;
-        return parseCallExpression();
+        if (isCallExpression())
+            return parseCallExpression();
+        let preIncrement = lexer.backtrackingScope(parsePreIncrement);
+        if (preIncrement)
+            return preIncrement;
+        let postIncrement = lexer.backtrackingScope(parsePostIncrement);
+        if (postIncrement)
+            return postIncrement;
+        return parseAssignment();
     }
     
     function genericParseCommaExpression(finalExpressionParser)
index 4439101..818535d 100644 (file)
@@ -276,6 +276,7 @@ class Rewriter extends VisitorBase {
     {
         return new FunctionLikeBlock(
             node.origin,
+            node.returnType ? node.returnType.visit(this) : null,
             node.argumentList.map(argument => argument.visit(this)),
             node.parameters.map(parameter => parameter.visit(this)),
             node.body.visit(this));
@@ -309,5 +310,17 @@ class Rewriter extends VisitorBase {
             node.increment ? node.increment.visit(this) : undefined,
             node.body.visit(this));
     }
+    
+    visitLetExpression(node)
+    {
+        let result = new LetExpression(node.origin);
+        result.index = node.index;
+        result.type = node.type ? node.type.visit(this) : null;
+        result.argument = node.argument.visit(this);
+        this._mapNode(node, result);
+        result.body = node.body.visit(this);
+        result.ePtr = node.ePtr;
+        return result;
+    }
 }
 
index 047c5a9..a3ad8dc 100644 (file)
@@ -43,6 +43,10 @@ native primitive typedef double;
 
 native int operator+(int, int);
 native uint operator+(uint, uint);
+int operator++(int value) { return value + 1; }
+uint operator++(uint value) { return value + 1; }
+int operator--(int value) { return value - 1; }
+uint operator--(uint value) { return value - 1; }
 native int operator-(int, int);
 native uint operator-(uint, uint);
 native int operator*(int, int);
index bc3ae20..37074b1 100644 (file)
@@ -60,6 +60,7 @@
 <script src="IntLiteral.js"></script>
 <script src="IntLiteralType.js"></script>
 <script src="Intrinsics.js"></script>
+<script src="LetExpression.js"></script>
 <script src="Lexer.js"></script>
 <script src="LexerToken.js"></script>
 <script src="LiteralTypeChecker.js"></script>
index 6c000c6..54a285e 100644 (file)
@@ -2091,6 +2091,190 @@ function TEST_protocolExtendsTwo()
     checkInt(program, callFunction(program, "thingy", [], [makeInt(program, 642)]), 642 + 743 + 91 + 39);
 }
 
+function TEST_prefixPlusPlus()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            ++x;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
+}
+
+function TEST_prefixPlusPlusResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return ++x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
+}
+
+function TEST_postfixPlusPlus()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            x++;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
+}
+
+function TEST_postfixPlusPlusResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return x++;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 64);
+}
+
+function TEST_prefixMinusMinus()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            --x;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
+}
+
+function TEST_prefixMinusMinusResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return --x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
+}
+
+function TEST_postfixMinusMinus()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            x--;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
+}
+
+function TEST_postfixMinusMinusResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return x--;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 64);
+}
+
+function TEST_plusEquals()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            x += 42;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 + 42);
+}
+
+function TEST_plusEqualsResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return x += 42;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 + 42);
+}
+
+function TEST_minusEquals()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            x -= 42;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 - 42);
+}
+
+function TEST_minusEqualsResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return x -= 42;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 - 42);
+}
+
+function TEST_timesEquals()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            x *= 42;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 * 42);
+}
+
+function TEST_timesEqualsResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return x *= 42;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 * 42);
+}
+
+function TEST_divideEquals()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            x /= 42;
+            return x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), (385 / 42) | 0);
+}
+
+function TEST_divideEqualsResult()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            return x /= 42;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), (385 / 42) | 0);
+}
+
 function TEST_twoIntLiterals()
 {
     let program = doPrep(`
index 76525cf..4dd0ee5 100644 (file)
@@ -206,8 +206,6 @@ class Visitor extends VisitorBase {
     
     visitVariableRef(node)
     {
-        if (node.variable)
-            node.variable.visit(this);
     }
     
     visitIfStatement(node)
@@ -304,11 +302,19 @@ class Visitor extends VisitorBase {
     
     visitFunctionLikeBlock(node)
     {
+        if (node.returnType)
+            node.returnType.visit(this);
         for (let argument of node.argumentList)
             argument.visit(this);
         for (let parameter of node.parameters)
             parameter.visit(this);
         node.body.visit(this);
     }
+    
+    visitLetExpression(node)
+    {
+        node.argument.visit(this);
+        node.body.visit(this);
+    }
 }