WSL should support enum
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Sep 2017 03:44:25 +0000 (03:44 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Sep 2017 03:44:25 +0000 (03:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176977

Reviewed by Myles Maxfield.

This implements enum. Enums members are referenced Java-style. For example, if we have an enum like:

    enum Bank {
        GP,
        FP
    }

Then you refer to the members by saying Bank.GP and Bank.FP.

Also adds a hack that makes operator- on a literal fold the literal. This fixes two issues:

- Lets you use negative literals in constexprs.
- Means that negative literals also get literal type unification.

WSL's constexpr support is really thin, but I think it's all we really need for now.

* WebGPUShadingLanguageRI/All.js:
* WebGPUShadingLanguageRI/Checker.js:
(Checker.prototype.visitEnumType):
(Checker.prototype.visitEnumLiteral):
* WebGPUShadingLanguageRI/ConstexprFolder.js: Added.
(ConstexprFolder.prototype.visitCallExpression):
(ConstexprFolder.prototype.visitTypeOrVariableRef):
(ConstexprFolder):
* WebGPUShadingLanguageRI/CreateLiteral.js:
(createLiteral.GenericLiteral.prototype.get valueForSelectedType):
(createLiteral.GenericLiteral.prototype.get negConstexpr):
* WebGPUShadingLanguageRI/DoubleLiteral.js:
(let.DoubleLiteral.createLiteral.negConstexpr):
* WebGPUShadingLanguageRI/EBufferBuilder.js:
(EBufferBuilder.prototype.visitEnumLiteral):
* WebGPUShadingLanguageRI/EnumLiteral.js: Added.
(EnumLiteral):
(EnumLiteral.prototype.get member):
(EnumLiteral.prototype.get type):
(EnumLiteral.prototype.get isConstexpr):
(EnumLiteral.prototype.unifyImpl):
(EnumLiteral.prototype.toString):
* WebGPUShadingLanguageRI/EnumMember.js: Added.
(EnumMember):
(EnumMember.prototype.get origin):
(EnumMember.prototype.get name):
(EnumMember.prototype.toString):
* WebGPUShadingLanguageRI/EnumType.js: Added.
(EnumType):
(EnumType.prototype.add):
(EnumType.prototype.get origin):
(EnumType.prototype.get name):
(EnumType.prototype.get baseType):
(EnumType.prototype.get memberNames):
(EnumType.prototype.memberByName):
(EnumType.prototype.get members):
(EnumType.prototype.get memberMap):
(EnumType.prototype.get isPrimitive):
(EnumType.prototype.populateDefaultValue):
(EnumType.prototype.get size):
(EnumType.prototype.toString):
* WebGPUShadingLanguageRI/Evaluator.js:
(Evaluator.prototype.visitGenericLiteral):
(Evaluator.prototype.visitEnumLiteral):
* WebGPUShadingLanguageRI/FloatLiteral.js:
(let.FloatLiteral.createLiteral.negConstexpr):
* WebGPUShadingLanguageRI/FoldConstexprs.js: Added.
(foldConstexprs):
* WebGPUShadingLanguageRI/IntLiteral.js:
(let.IntLiteral.createLiteral.negConstexpr):
* WebGPUShadingLanguageRI/Intrinsics.js:
(Intrinsics):
* WebGPUShadingLanguageRI/NameResolver.js:
(NameResolver.prototype.visitProtocolDecl):
* WebGPUShadingLanguageRI/Parse.js:
(parseConstexpr):
(parsePossibleSuffix):
(parseEnumMember):
(parseEnumType):
(parse):
* WebGPUShadingLanguageRI/Prepare.js:
(prepare):
* WebGPUShadingLanguageRI/Rewriter.js:
(Rewriter.prototype.visitEnumType):
(Rewriter.prototype.visitEnumMember):
(Rewriter.prototype.visitEnumLiteral):
* WebGPUShadingLanguageRI/StructType.js:
(StructType.prototype.get origin):
(StructType.prototype.instantiate):
* WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js: Added.
(synthesizeEnumFunctions):
* WebGPUShadingLanguageRI/Test.html:
* WebGPUShadingLanguageRI/Test.js:
(checkEnum):
(TEST_simpleEnum):
(TEST_enumWithManualValues):
(TEST_enumWithoutZero):
(TEST_enumConstexprGenericFunction):
(TEST_enumConstexprGenericStruct):
* WebGPUShadingLanguageRI/UintLiteral.js:
(let.UintLiteral.createLiteral.negConstexpr):
* WebGPUShadingLanguageRI/Visitor.js:
(Visitor.prototype.visitProtocolDecl):

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

26 files changed:
Tools/ChangeLog
Tools/WebGPUShadingLanguageRI/All.js
Tools/WebGPUShadingLanguageRI/Checker.js
Tools/WebGPUShadingLanguageRI/ConstexprFolder.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/CreateLiteral.js
Tools/WebGPUShadingLanguageRI/DoubleLiteral.js
Tools/WebGPUShadingLanguageRI/EBufferBuilder.js
Tools/WebGPUShadingLanguageRI/EnumLiteral.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/EnumMember.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/EnumType.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/Evaluator.js
Tools/WebGPUShadingLanguageRI/FloatLiteral.js
Tools/WebGPUShadingLanguageRI/FoldConstexprs.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/IntLiteral.js
Tools/WebGPUShadingLanguageRI/Intrinsics.js
Tools/WebGPUShadingLanguageRI/NameResolver.js
Tools/WebGPUShadingLanguageRI/Parse.js
Tools/WebGPUShadingLanguageRI/Prepare.js
Tools/WebGPUShadingLanguageRI/Rewriter.js
Tools/WebGPUShadingLanguageRI/StatementCloner.js
Tools/WebGPUShadingLanguageRI/StructType.js
Tools/WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/Test.html
Tools/WebGPUShadingLanguageRI/Test.js
Tools/WebGPUShadingLanguageRI/UintLiteral.js
Tools/WebGPUShadingLanguageRI/Visitor.js

index 0af8baa..6834826 100644 (file)
@@ -1,5 +1,112 @@
 2017-09-18  Filip Pizlo  <fpizlo@apple.com>
 
+        WSL should support enum
+        https://bugs.webkit.org/show_bug.cgi?id=176977
+
+        Reviewed by Myles Maxfield.
+        
+        This implements enum. Enums members are referenced Java-style. For example, if we have an enum like:
+        
+            enum Bank {
+                GP,
+                FP
+            }
+        
+        Then you refer to the members by saying Bank.GP and Bank.FP.
+        
+        Also adds a hack that makes operator- on a literal fold the literal. This fixes two issues:
+        
+        - Lets you use negative literals in constexprs.
+        - Means that negative literals also get literal type unification.
+        
+        WSL's constexpr support is really thin, but I think it's all we really need for now.
+        
+        * WebGPUShadingLanguageRI/All.js:
+        * WebGPUShadingLanguageRI/Checker.js:
+        (Checker.prototype.visitEnumType):
+        (Checker.prototype.visitEnumLiteral):
+        * WebGPUShadingLanguageRI/ConstexprFolder.js: Added.
+        (ConstexprFolder.prototype.visitCallExpression):
+        (ConstexprFolder.prototype.visitTypeOrVariableRef):
+        (ConstexprFolder):
+        * WebGPUShadingLanguageRI/CreateLiteral.js:
+        (createLiteral.GenericLiteral.prototype.get valueForSelectedType):
+        (createLiteral.GenericLiteral.prototype.get negConstexpr):
+        * WebGPUShadingLanguageRI/DoubleLiteral.js:
+        (let.DoubleLiteral.createLiteral.negConstexpr):
+        * WebGPUShadingLanguageRI/EBufferBuilder.js:
+        (EBufferBuilder.prototype.visitEnumLiteral):
+        * WebGPUShadingLanguageRI/EnumLiteral.js: Added.
+        (EnumLiteral):
+        (EnumLiteral.prototype.get member):
+        (EnumLiteral.prototype.get type):
+        (EnumLiteral.prototype.get isConstexpr):
+        (EnumLiteral.prototype.unifyImpl):
+        (EnumLiteral.prototype.toString):
+        * WebGPUShadingLanguageRI/EnumMember.js: Added.
+        (EnumMember):
+        (EnumMember.prototype.get origin):
+        (EnumMember.prototype.get name):
+        (EnumMember.prototype.toString):
+        * WebGPUShadingLanguageRI/EnumType.js: Added.
+        (EnumType):
+        (EnumType.prototype.add):
+        (EnumType.prototype.get origin):
+        (EnumType.prototype.get name):
+        (EnumType.prototype.get baseType):
+        (EnumType.prototype.get memberNames):
+        (EnumType.prototype.memberByName):
+        (EnumType.prototype.get members):
+        (EnumType.prototype.get memberMap):
+        (EnumType.prototype.get isPrimitive):
+        (EnumType.prototype.populateDefaultValue):
+        (EnumType.prototype.get size):
+        (EnumType.prototype.toString):
+        * WebGPUShadingLanguageRI/Evaluator.js:
+        (Evaluator.prototype.visitGenericLiteral):
+        (Evaluator.prototype.visitEnumLiteral):
+        * WebGPUShadingLanguageRI/FloatLiteral.js:
+        (let.FloatLiteral.createLiteral.negConstexpr):
+        * WebGPUShadingLanguageRI/FoldConstexprs.js: Added.
+        (foldConstexprs):
+        * WebGPUShadingLanguageRI/IntLiteral.js:
+        (let.IntLiteral.createLiteral.negConstexpr):
+        * WebGPUShadingLanguageRI/Intrinsics.js:
+        (Intrinsics):
+        * WebGPUShadingLanguageRI/NameResolver.js:
+        (NameResolver.prototype.visitProtocolDecl):
+        * WebGPUShadingLanguageRI/Parse.js:
+        (parseConstexpr):
+        (parsePossibleSuffix):
+        (parseEnumMember):
+        (parseEnumType):
+        (parse):
+        * WebGPUShadingLanguageRI/Prepare.js:
+        (prepare):
+        * WebGPUShadingLanguageRI/Rewriter.js:
+        (Rewriter.prototype.visitEnumType):
+        (Rewriter.prototype.visitEnumMember):
+        (Rewriter.prototype.visitEnumLiteral):
+        * WebGPUShadingLanguageRI/StructType.js:
+        (StructType.prototype.get origin):
+        (StructType.prototype.instantiate):
+        * WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js: Added.
+        (synthesizeEnumFunctions):
+        * WebGPUShadingLanguageRI/Test.html:
+        * WebGPUShadingLanguageRI/Test.js:
+        (checkEnum):
+        (TEST_simpleEnum):
+        (TEST_enumWithManualValues):
+        (TEST_enumWithoutZero):
+        (TEST_enumConstexprGenericFunction):
+        (TEST_enumConstexprGenericStruct):
+        * WebGPUShadingLanguageRI/UintLiteral.js:
+        (let.UintLiteral.createLiteral.negConstexpr):
+        * WebGPUShadingLanguageRI/Visitor.js:
+        (Visitor.prototype.visitProtocolDecl):
+
+2017-09-18  Filip Pizlo  <fpizlo@apple.com>
+
         WSL prepare() should cache the parsed standard library
         https://bugs.webkit.org/show_bug.cgi?id=177118
 
index 0bf9100..e811765 100644 (file)
@@ -56,6 +56,7 @@ load("CheckWrapped.js");
 load("Checker.js");
 load("CloneProgram.js");
 load("CommaExpression.js");
+load("ConstexprFolder.js");
 load("ConstexprTypeParameter.js");
 load("Continue.js");
 load("ConvertPtrToArrayRefExpression.js");
@@ -68,6 +69,9 @@ load("EArrayRef.js");
 load("EBuffer.js");
 load("EBufferBuilder.js");
 load("EPtr.js");
+load("EnumLiteral.js");
+load("EnumMember.js");
+load("EnumType.js");
 load("EvaluationCommon.js");
 load("Evaluator.js");
 load("ExpressionFinder.js");
@@ -76,6 +80,7 @@ load("FindHighZombies.js");
 load("FlattenProtocolExtends.js");
 load("FloatLiteral.js");
 load("FloatLiteralType.js");
+load("FoldConstexprs.js");
 load("ForLoop.js");
 load("Func.js");
 load("FuncDef.js");
@@ -134,6 +139,7 @@ load("StatementCloner.js");
 load("StructLayoutBuilder.js");
 load("StructType.js");
 load("Substitution.js");
+load("SynthesizeEnumFunctions.js");
 load("SynthesizeStructAccessors.js");
 load("TypeDef.js");
 load("TypeDefResolver.js");
index d841213..398dc2e 100644 (file)
@@ -79,6 +79,56 @@ class Checker extends Visitor {
         }
     }
     
+    visitEnumType(node)
+    {
+        node.baseType.visit(this);
+        
+        let baseType = node.baseType.unifyNode;
+        
+        if (!baseType.isInt)
+            throw new WTypeError(node.origin.originString, "Base type of enum is not an integer: " + node.baseType);
+        
+        for (let member of node.members) {
+            if (!member.value)
+                continue;
+            
+            let memberType = member.value.visit(this);
+            if (!baseType.equalsWithCommit(memberType))
+                throw new WTypeError(member.origin.originString, "Type of enum member " + member.value.name + " does not patch enum base type (member type is " + memberType + ", enum base type is " + node.baseType + ")");
+        }
+        
+        let nextValue = baseType.defaultValue;
+        for (let member of node.members) {
+            if (member.value) {
+                nextValue = baseType.successorValue(member.value.unifyNode.valueForSelectedType);
+                continue;
+            }
+            
+            member.value = baseType.createLiteral(member.origin, nextValue);
+            nextValue = baseType.successorValue(nextValue);
+        }
+        
+        let memberArray = Array.from(node.members);
+        for (let i = 0; i < memberArray.length; ++i) {
+            let member = memberArray[i];
+            for (let j = i + 1; j < memberArray.length; ++j) {
+                let otherMember = memberArray[j];
+                if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, otherMember.value.unifyNode.valueForSelectedType))
+                    throw new WTypeError(otherMember.origin.originString, "Duplicate enum member value (" + member.name + " has " + member.value + " while " + otherMember.name + " has " + otherMember.value + ")");
+            }
+        }
+        
+        let foundZero = false;
+        for (let member of node.members) {
+            if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, baseType.defaultValue)) {
+                foundZero = true;
+                break;
+            }
+        }
+        if (!foundZero)
+            throw new WTypeError(node.origin.originString, "Enum does not have a member with the value zero");
+    }
+    
     _checkTypeArguments(origin, typeParameters, typeArguments)
     {
         for (let i = 0; i < typeParameters.length; ++i) {
@@ -345,6 +395,11 @@ class Checker extends Visitor {
     {
         return this._program.intrinsics.bool;
     }
+    
+    visitEnumLiteral(node)
+    {
+        return node.member.enumType;
+    }
 
     _requireBool(expression)
     {
diff --git a/Tools/WebGPUShadingLanguageRI/ConstexprFolder.js b/Tools/WebGPUShadingLanguageRI/ConstexprFolder.js
new file mode 100644 (file)
index 0000000..1e5ad66
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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";
+
+// NOTE: This doesn't let you negate constexpr variables. I think that's probably OK for now, but we should
+// fix that eventually.
+// https://bugs.webkit.org/show_bug.cgi?id=177121
+
+class ConstexprFolder extends Visitor {
+    visitCallExpression(node)
+    {
+        super.visitCallExpression(node);
+        
+        if (node.name == "operator-"
+            && !node.typeArguments.length
+            && node.argumentList.length == 1
+            && node.argumentList[0].unifyNode.isConstexpr
+            && node.argumentList[0].unifyNode.negConstexpr) {
+            node.become(node.argumentList[0].unifyNode.negConstexpr(node.origin));
+            return;
+        }
+    }
+    
+    visitTypeOrVariableRef(node)
+    {
+    }
+}
+
index 1118e44..c470d93 100644 (file)
@@ -42,9 +42,31 @@ function createLiteral(config)
         }
         
         get value() { return this._value; }
+        
+        // This is necessary because once we support int64, we'll need that to be represented as an object
+        // rather than as a primitive. Then we'll need to convert.
+        get valueForSelectedType()
+        {
+            let type = this.type.type.unifyNode;
+            if (!type)
+                throw new Error("Cannot get type for " + this);
+            let func = type["formatValueFrom" + config.literalClassName];
+            if (!func)
+                throw new Error("Cannot get function to format type for " + config.literalClassName + " from " + type);
+            return func.call(type, this.value);
+        }
+        
         get isConstexpr() { return true; }
         get isLiteral() { return true; }
         
+        get negConstexpr()
+        {
+            if (!config.negConstexpr)
+                return null;
+            
+            return () => config.negConstexpr.call(this);
+        }
+        
         unifyImpl(unificationContext, other)
         {
             if (!(other instanceof GenericLiteral))
index e9fb6ed..7fd9813 100644 (file)
 "use strict";
 
 let DoubleLiteral = createLiteral({
+    literalClassName: "DoubleLiteral",
     preferredTypeName: "double",
     
+    negConstexpr(origin)
+    {
+        return new IntLiteral(origin, -this.value);
+    },
+    
     createType(origin, value)
     {
         return new DoubleLiteralType(origin, value);
index 1302643..3850e76 100644 (file)
@@ -98,6 +98,11 @@ class EBufferBuilder extends Visitor {
         node.ePtr = EPtr.box();
     }
     
+    visitEnumLiteral(node)
+    {
+        node.ePtr = EPtr.box();
+    }
+    
     visitLogicalNot(node)
     {
         node.ePtr = EPtr.box();
diff --git a/Tools/WebGPUShadingLanguageRI/EnumLiteral.js b/Tools/WebGPUShadingLanguageRI/EnumLiteral.js
new file mode 100644 (file)
index 0000000..6ef879b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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";
+
+class EnumLiteral extends Expression {
+    constructor(origin, member)
+    {
+        super(origin);
+        this._member = member;
+    }
+    
+    get member() { return this._member; }
+    get type() { return this.member.enumType; }
+    get isConstexpr() { return true; }
+    
+    unifyImpl(unificationContext, other)
+    {
+        if (!(other instanceof EnumLiteral))
+            return false;
+        return this.member == other.member;
+    }
+        
+    toString()
+    {
+        return this.member.enumType.name + "." + this.member.name;
+    }
+}
diff --git a/Tools/WebGPUShadingLanguageRI/EnumMember.js b/Tools/WebGPUShadingLanguageRI/EnumMember.js
new file mode 100644 (file)
index 0000000..077abce
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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";
+
+class EnumMember extends Node {
+    constructor(origin, name, value)
+    {
+        super();
+        this._origin = origin;
+        this._name = name;
+        this.value = value;
+    }
+    
+    get origin() { return this._origin; }
+    get name() { return this._name; }
+    
+    toString()
+    {
+        let result = this.name;
+        if (this.value)
+            result += " = " + this.value;
+        return result;
+    }
+}
diff --git a/Tools/WebGPUShadingLanguageRI/EnumType.js b/Tools/WebGPUShadingLanguageRI/EnumType.js
new file mode 100644 (file)
index 0000000..33cca9a
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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";
+
+class EnumType extends Type {
+    constructor(origin, name, baseType)
+    {
+        super();
+        this._origin = origin;
+        this._name = name;
+        this._baseType = baseType;
+        this._members = new Map();
+    }
+    
+    add(member)
+    {
+        if (this._members.has(member.name))
+            throw new WTypeError(member.origin.originString, "Duplicate enum member name: " + member.name);
+        member.enumType = this;
+        this._members.set(member.name, member);
+    }
+    
+    get origin() { return this._origin; }
+    get name() { return this._name; }
+    get baseType() { return this._baseType; }
+    
+    get memberNames() { return this._members.keys(); }
+    memberByName(name) { return this._members.get(name); }
+    get members() { return this._members.values(); }
+    get memberMap() { return this._members; }
+    
+    get isPrimitive() { return true; }
+    
+    populateDefaultValue(buffer, offset)
+    {
+        this.baseType.populateDefaultValue(buffer, offset);
+    }
+    
+    get size() { return this.baseType.size; }
+    
+    toString()
+    {
+        return "enum " + this.name + " : " + this.baseType + " { " + Array.from(this.members).join(",") + " }";
+    }
+}
+
index a5805bd..8e9a315 100644 (file)
@@ -147,7 +147,7 @@ class Evaluator extends Visitor {
     
     visitGenericLiteral(node)
     {
-        return node.ePtr.box(node.value);
+        return node.ePtr.box(node.valueForSelectedType);
     }
     
     visitNullLiteral(node)
@@ -159,6 +159,11 @@ class Evaluator extends Visitor {
     {
         return node.ePtr.box(node.value);
     }
+    
+    visitEnumLiteral(node)
+    {
+        return node.ePtr.box(node.member.value.unifyNode.valueForSelectedType);
+    }
 
     visitLogicalNot(node)
     {
index 8779d20..e6c5737 100644 (file)
 "use strict";
 
 let FloatLiteral = createLiteral({
+    literalClassName: "FloatLiteral",
     preferredTypeName: "float",
     
+    negConstexpr(origin)
+    {
+        return new IntLiteral(origin, -this.value);
+    },
+    
     createType(origin, value)
     {
         return new FloatLiteralType(origin, value);
diff --git a/Tools/WebGPUShadingLanguageRI/FoldConstexprs.js b/Tools/WebGPUShadingLanguageRI/FoldConstexprs.js
new file mode 100644 (file)
index 0000000..1107a78
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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";
+
+function foldConstexprs(program)
+{
+    program.visit(new ConstexprFolder());
+}
+
index 9b7e0ee..022b343 100644 (file)
 "use strict";
 
 let IntLiteral = createLiteral({
+    literalClassName: "IntLiteral",
     preferredTypeName: "int",
     
+    negConstexpr(origin)
+    {
+        return new IntLiteral(origin, (-this.value) | 0);
+    },
+    
     createType(origin, value)
     {
         return new IntLiteralType(origin, value);
index caf5f60..5cfa34e 100644 (file)
@@ -69,7 +69,13 @@ class Intrinsics {
                 type.isSigned = true;
                 type.canRepresent = value => isBitwiseEquivalent(value | 0, value);
                 type.size = 1;
+                type.defaultValue = 0;
+                type.createLiteral = (origin, value) => IntLiteral.withType(origin, value | 0, type);
+                type.successorValue = value => (value + 1) | 0;
+                type.valuesEqual = (a, b) => a === b;
                 type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
+                type.formatValueFromIntLiteral = value => value | 0;
+                type.formatValueFromUintLiteral = value => value | 0;
             });
 
         this._map.set(
@@ -81,7 +87,13 @@ class Intrinsics {
                 type.isSigned = false;
                 type.canRepresent = value => isBitwiseEquivalent(value >>> 0, value);
                 type.size = 1;
+                type.defaultValue = 0;
+                type.createLiteral = (origin, value) => IntLiteral.withType(origin, value >>> 0, type);
+                type.successorValue = value => (value + 1) >>> 0;
+                type.valuesEqual = (a, b) => a === b;
                 type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
+                type.formatValueFromIntLiteral = value => value >>> 0;
+                type.formatValueFromUintLiteral = value => value >>> 0;
             });
 
         this._map.set(
@@ -93,6 +105,10 @@ class Intrinsics {
                 type.isNumber = true;
                 type.canRepresent = value => isBitwiseEquivalent(Math.fround(value), value);
                 type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
+                type.formatValueFromIntLiteral = value => value;
+                type.formatValueFromUintLiteral = value => value;
+                type.formatValueFromFloatLiteral = value => Math.fround(value);
+                type.formatValueFromDoubleLiteral = value => Math.fround(value);
             });
 
         this._map.set(
@@ -104,6 +120,10 @@ class Intrinsics {
                 type.isNumber = true;
                 type.canRepresent = value => true;
                 type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
+                type.formatValueFromIntLiteral = value => value;
+                type.formatValueFromUintLiteral = value => value;
+                type.formatValueFromFloatLiteral = value => value;
+                type.formatValueFromDoubleLiteral = value => value;
             });
 
         this._map.set(
index ba2102d..694d4a8 100644 (file)
@@ -255,6 +255,18 @@ class NameResolver extends Visitor {
     
     visitDotExpression(node)
     {
+        // This could be a reference to an enum. Let's resolve that now.
+        if (node.struct instanceof VariableRef) {
+            let enumType = this._nameContext.get(Type, node.struct.name);
+            if (enumType && enumType instanceof EnumType) {
+                let enumMember = enumType.memberByName(node.fieldName);
+                if (!enumMember)
+                    throw new WTypeError(node.origin.originString, "Enum " + enumType.name + " does not have a member named " + node.fieldName);
+                node.become(new EnumLiteral(node.origin, enumMember));
+                return;
+            }
+        }
+        
         this._handlePropertyAccess(node);
         super.visitDotExpression(node);
     }
index 4dbd0b9..6b017fa 100644 (file)
@@ -227,7 +227,13 @@ function parse(program, origin, originKind, lineNumberOffset, text)
     
     function parseConstexpr()
     {
-        return parseTerm();
+        let token;
+        if (token = tryConsume("-"))
+            return new CallExpression(token, "operator" + token.text, [], [parseTerm()]);
+        let left = parseTerm();
+        if (token = tryConsume("."))
+            left = new DotExpression(token, left, consumeKind("identifier").text);
+        return left;
     }
     
     function parseTypeArguments()
@@ -396,12 +402,17 @@ function parse(program, origin, originKind, lineNumberOffset, text)
     
     function parsePossibleSuffix()
     {
-        if (isCallExpression())
-            return parseCallExpression();
+        let acceptableOperators = ["++", "--", ".", "->", "["];
+        let limitedOperators = [".", "->", "["];
+        let left;
+        if (isCallExpression()) {
+            left = parseCallExpression();
+            acceptableOperators = limitedOperators;
+        } else
+            left = parseTerm();
         
-        let left = parseTerm();
         let token;
-        while (token = tryConsume("++", "--", ".", "->", "[")) {
+        while (token = tryConsume(...acceptableOperators)) {
             switch (token.text) {
             case "++":
             case "--":
@@ -422,6 +433,7 @@ function parse(program, origin, originKind, lineNumberOffset, text)
             default:
                 throw new Error("Bad token: " + token);
             }
+            acceptableOperators = limitedOperators;
         }
         return left;
     }
@@ -931,6 +943,35 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         return result;
     }
     
+    function parseEnumMember()
+    {
+        let name = consumeKind("identifier");
+        let value = null;
+        if (tryConsume("="))
+            value = parseConstexpr();
+        return new EnumMember(name, name.text, value);
+    }
+    
+    function parseEnumType()
+    {
+        consume("enum");
+        let name = consumeKind("identifier");
+        let baseType;
+        if (tryConsume(":"))
+            baseType = parseType();
+        else
+            baseType = new TypeRef(name, "int", []);
+        consume("{");
+        let result = new EnumType(name, name.text, baseType);
+        while (!test("}")) {
+            result.add(parseEnumMember());
+            if (!tryConsume(","))
+                break;
+        }
+        consume("}");
+        return result;
+    }
+    
     for (;;) {
         let token = lexer.peek();
         if (!token)
@@ -946,7 +987,7 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         else if (token.text == "struct")
             program.add(parseStructType());
         else if (token.text == "enum")
-            program.add(parseEnum());
+            program.add(parseEnumType());
         else if (token.text == "protocol")
             program.add(parseProtocolDecl());
         else
index ec48bee..6b58a99 100644 (file)
@@ -35,6 +35,7 @@ let prepare = (() => {
         let program = cloneProgram(standardProgram);
         parse(program, origin, "user", lineNumberOffset, text);
         
+        foldConstexprs(program);
         let nameResolver = createNameResolver(program);
         resolveNamesInTypes(program, nameResolver);
         resolveNamesInProtocols(program, nameResolver);
@@ -43,6 +44,7 @@ let prepare = (() => {
         // FIXME: Need to verify that structre are not cyclic.
         // https://bugs.webkit.org/show_bug.cgi?id=177044
         synthesizeStructAccessors(program);
+        synthesizeEnumFunctions(program);
         resolveNamesInFunctions(program, nameResolver);
         resolveTypeDefsInFunctions(program);
         
index d7b9a53..ec25d40 100644 (file)
@@ -59,6 +59,7 @@ class Rewriter {
     visitStructType(node) { return node; }
     visitConstexprTypeParameter(node) { return node; }
     visitProtocolDecl(node) { return node; }
+    visitEnumType(node) { return node; }
 
     // This is almost wrong. We instantiate Func in Substitution in ProtocolDecl. Then, we end up
     // not rewriting type variables. I think that just works because not rewriting them there is OK.
@@ -141,6 +142,18 @@ class Rewriter {
         return new Field(node.origin, node.name, node.type.visit(this));
     }
     
+    visitEnumMember(node)
+    {
+        return new EnumMember(node.origin, node.name, node.value.visit(this));
+    }
+    
+    visitEnumLiteral(node)
+    {
+        let result = new EnumLiteral(node.origin, node.member);
+        result.ePtr = node.ePtr;
+        return result;
+    }
+    
     visitReferenceType(node)
     {
         return new node.constructor(node.rogiin, node.addressSpace, node.elementType.visit(this));
index e860789..8241756 100644 (file)
@@ -109,5 +109,13 @@ class StatementCloner extends Rewriter {
     {
         return new TypeOrVariableRef(node.origin, node.name);
     }
+    
+    visitEnumType(node)
+    {
+        let result = new EnumType(node.origin, node.name, node.baseType.visit(this));
+        for (let member of node.members)
+            result.add(member);
+        return result;
+    }
 }
 
index 3ab7514..217922e 100644 (file)
@@ -43,6 +43,7 @@ class StructType extends Type {
     }
     
     get name() { return this._name; }
+    get origin() { return this._origin; }
     get typeParameters() { return this._typeParameters; }
     
     get fieldNames() { return this._fields.keys(); }
@@ -64,7 +65,7 @@ class StructType extends Type {
         
         if (typeArguments) {
             if (typeArguments.length != this.typeParameters.length)
-                throw new WTypeError(origin.originString, "Wrong number of type arguments to instantiation");
+                throw new WTypeError(this.origin.originString, "Wrong number of type arguments to instantiation");
             
             if (!typeArguments.length)
                 return this;
diff --git a/Tools/WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js b/Tools/WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js
new file mode 100644 (file)
index 0000000..5af2bdf
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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";
+
+function synthesizeEnumFunctions(program)
+{
+    for (let type of program.types.values()) {
+        if (!(type instanceof EnumType))
+            continue;
+        
+        let nativeFunc;
+        let isCast = false;
+        let shaderType;
+        
+        nativeFunc = new NativeFunc(
+            type.origin, "operator==", new TypeRef(type.origin, "bool", []), [],
+            [
+                new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name, [])),
+                new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name, []))
+            ],
+            isCast, shaderType);
+        nativeFunc.implementation = ([left, right]) => EPtr.box(left.loadValue() == right.loadValue());
+        program.add(nativeFunc);
+        
+        nativeFunc = new NativeFunc(
+            type.origin, "operator.value", type.baseType.visit(new Rewriter()), [],
+            [new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name, []))],
+            isCast, shaderType);
+        nativeFunc.implementation = ([value]) => value;
+        program.add(nativeFunc);
+        
+        nativeFunc = new NativeFunc(
+            type.origin, "operator cast", type.baseType.visit(new Rewriter()), [],
+            [new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name, []))],
+            isCast, shaderType);
+        nativeFunc.implementation = ([value]) => value;
+        program.add(nativeFunc);
+        
+        nativeFunc = new NativeFunc(
+            type.origin, "operator cast", new TypeRef(type.origin, type.name, []), [],
+            [new FuncParameter(type.origin, null, type.baseType.visit(new Rewriter()))],
+            isCast, shaderType);
+        nativeFunc.implementation = ([value]) => value;
+        program.add(nativeFunc);
+    }
+}
index e632acd..62da230 100644 (file)
@@ -33,6 +33,7 @@
 <script src="Checker.js"></script>
 <script src="CloneProgram.js"></script>
 <script src="CommaExpression.js"></script>
+<script src="ConstexprFolder.js"></script>
 <script src="ConstexprTypeParameter.js"></script>
 <script src="Continue.js"></script>
 <script src="ConvertPtrToArrayRefExpression.js"></script>
@@ -45,6 +46,9 @@
 <script src="EBuffer.js"></script>
 <script src="EBufferBuilder.js"></script>
 <script src="EPtr.js"></script>
+<script src="EnumLiteral.js"></script>
+<script src="EnumMember.js"></script>
+<script src="EnumType.js"></script>
 <script src="EvaluationCommon.js"></script>
 <script src="Evaluator.js"></script>
 <script src="ExpressionFinder.js"></script>
@@ -53,6 +57,7 @@
 <script src="FlattenProtocolExtends.js"></script>
 <script src="FloatLiteral.js"></script>
 <script src="FloatLiteralType.js"></script>
+<script src="FoldConstexprs.js"></script>
 <script src="ForLoop.js"></script>
 <script src="Func.js"></script>
 <script src="FuncDef.js"></script>
 <script src="StructLayoutBuilder.js"></script>
 <script src="StructType.js"></script>
 <script src="Substitution.js"></script>
+<script src="SynthesizeEnumFunctions.js"></script>
 <script src="SynthesizeStructAccessors.js"></script>
 <script src="TypeDef.js"></script>
 <script src="TypeDefResolver.js"></script>
index e21bd8c..97bceee 100644 (file)
@@ -101,6 +101,14 @@ function checkInt(program, result, expected)
     checkNumber(program, result, expected);
 }
 
+function checkEnum(program, result, expected)
+{
+    if (!(result.type instanceof EnumType))
+        throw new Error("Wrong result type; result: " + result);
+    if (result.value != expected)
+        throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
+}
+
 function checkUint(program, result, expected)
 {
     if (!result.type.equals(program.intrinsics.uint32))
@@ -3894,6 +3902,281 @@ function TEST_builtinVectors()
     checkBool(program, callFunction(program, "food6", [], []), false);
 }
 
+function TEST_simpleEnum()
+{
+    let program = doPrep(`
+        enum Foo {
+            War,
+            Famine,
+            Pestilence,
+            Death
+        }
+        Foo war()
+        {
+            return Foo.War;
+        }
+        Foo famine()
+        {
+            return Foo.Famine;
+        }
+        Foo pestilence()
+        {
+            return Foo.Pestilence;
+        }
+        Foo death()
+        {
+            return Foo.Death;
+        }
+        bool equals(Foo a, Foo b)
+        {
+            return a == b;
+        }
+        bool notEquals(Foo a, Foo b)
+        {
+            return a != b;
+        }
+        bool testSimpleEqual()
+        {
+            return equals(Foo.War, Foo.War);
+        }
+        bool testAnotherEqual()
+        {
+            return equals(Foo.Pestilence, Foo.Pestilence);
+        }
+        bool testNotEqual()
+        {
+            return equals(Foo.Famine, Foo.Death);
+        }
+        bool testSimpleNotEqual()
+        {
+            return notEquals(Foo.War, Foo.War);
+        }
+        bool testAnotherNotEqual()
+        {
+            return notEquals(Foo.Pestilence, Foo.Pestilence);
+        }
+        bool testNotNotEqual()
+        {
+            return notEquals(Foo.Famine, Foo.Death);
+        }
+        int intWar()
+        {
+            return int(war());
+        }
+        int intFamine()
+        {
+            return int(famine());
+        }
+        int intPestilence()
+        {
+            return int(pestilence());
+        }
+        int intDeath()
+        {
+            return int(death());
+        }
+        int warValue()
+        {
+            return war().value;
+        }
+        int famineValue()
+        {
+            return famine().value;
+        }
+        int pestilenceValue()
+        {
+            return pestilence().value;
+        }
+        int deathValue()
+        {
+            return death().value;
+        }
+        int warValueLiteral()
+        {
+            return Foo.War.value;
+        }
+        int famineValueLiteral()
+        {
+            return Foo.Famine.value;
+        }
+        int pestilenceValueLiteral()
+        {
+            return Foo.Pestilence.value;
+        }
+        int deathValueLiteral()
+        {
+            return Foo.Death.value;
+        }
+        Foo intWarBackwards()
+        {
+            return Foo(intWar());
+        }
+        Foo intFamineBackwards()
+        {
+            return Foo(intFamine());
+        }
+        Foo intPestilenceBackwards()
+        {
+            return Foo(intPestilence());
+        }
+        Foo intDeathBackwards()
+        {
+            return Foo(intDeath());
+        }
+    `);
+    checkEnum(program, callFunction(program, "war", [], []), 0);
+    checkEnum(program, callFunction(program, "famine", [], []), 1);
+    checkEnum(program, callFunction(program, "pestilence", [], []), 2);
+    checkEnum(program, callFunction(program, "death", [], []), 3);
+    checkBool(program, callFunction(program, "testSimpleEqual", [], []), true);
+    checkBool(program, callFunction(program, "testAnotherEqual", [], []), true);
+    checkBool(program, callFunction(program, "testNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testSimpleNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testAnotherNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testNotNotEqual", [], []), true);
+    checkInt(program, callFunction(program, "intWar", [], []), 0);
+    checkInt(program, callFunction(program, "intFamine", [], []), 1);
+    checkInt(program, callFunction(program, "intPestilence", [], []), 2);
+    checkInt(program, callFunction(program, "intDeath", [], []), 3);
+    checkInt(program, callFunction(program, "warValue", [], []), 0);
+    checkInt(program, callFunction(program, "famineValue", [], []), 1);
+    checkInt(program, callFunction(program, "pestilenceValue", [], []), 2);
+    checkInt(program, callFunction(program, "deathValue", [], []), 3);
+    checkInt(program, callFunction(program, "warValueLiteral", [], []), 0);
+    checkInt(program, callFunction(program, "famineValueLiteral", [], []), 1);
+    checkInt(program, callFunction(program, "pestilenceValueLiteral", [], []), 2);
+    checkInt(program, callFunction(program, "deathValueLiteral", [], []), 3);
+    checkEnum(program, callFunction(program, "intWarBackwards", [], []), 0);
+    checkEnum(program, callFunction(program, "intFamineBackwards", [], []), 1);
+    checkEnum(program, callFunction(program, "intPestilenceBackwards", [], []), 2);
+    checkEnum(program, callFunction(program, "intDeathBackwards", [], []), 3);
+}
+
+function TEST_enumWithManualValues()
+{
+    let program = doPrep(`
+        enum Foo {
+            War = 72,
+            Famine = 0,
+            Pestilence = 23,
+            Death = -42
+        }
+        Foo war()
+        {
+            return Foo.War;
+        }
+        Foo famine()
+        {
+            return Foo.Famine;
+        }
+        Foo pestilence()
+        {
+            return Foo.Pestilence;
+        }
+        Foo death()
+        {
+            return Foo.Death;
+        }
+    `);
+    checkEnum(program, callFunction(program, "war", [], []), 72);
+    checkEnum(program, callFunction(program, "famine", [], []), 0);
+    checkEnum(program, callFunction(program, "pestilence", [], []), 23);
+    checkEnum(program, callFunction(program, "death", [], []), -42);
+}
+
+function TEST_enumWithoutZero()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo {
+                War = 72,
+                Famine = 64,
+                Pestilence = 23,
+                Death = -42
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_enumDuplicates()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo {
+                War = -42,
+                Famine = 0,
+                Pestilence = 23,
+                Death = -42
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_enumWithSomeManualValues()
+{
+    let program = doPrep(`
+        enum Foo {
+            War = 72,
+            Famine,
+            Pestilence = 0,
+            Death
+        }
+        Foo war()
+        {
+            return Foo.War;
+        }
+        Foo famine()
+        {
+            return Foo.Famine;
+        }
+        Foo pestilence()
+        {
+            return Foo.Pestilence;
+        }
+        Foo death()
+        {
+            return Foo.Death;
+        }
+    `);
+    checkEnum(program, callFunction(program, "war", [], []), 72);
+    checkEnum(program, callFunction(program, "famine", [], []), 73);
+    checkEnum(program, callFunction(program, "pestilence", [], []), 0);
+    checkEnum(program, callFunction(program, "death", [], []), 1);
+}
+
+function TEST_enumConstexprGenericFunction()
+{
+    let program = doPrep(`
+        enum Axis { X, Y }
+        int foo<Axis axis>() { return int(axis); }
+        int testX() { return foo<Axis.X>(); }
+        int testY() { return foo<Axis.Y>(); }
+    `);
+    checkInt(program, callFunction(program, "testX", [], []), 0);
+    checkInt(program, callFunction(program, "testY", [], []), 1);
+}
+
+function TEST_enumConstexprGenericStruct()
+{
+    let program = doPrep(`
+        enum Axis { X, Y }
+        struct Foo<Axis axis> { }
+        int foo<Axis axis>(Foo<axis>) { return int(axis); }
+        int testX()
+        {   
+            Foo<Axis.X> f;
+            return foo(f);
+        }
+        int testY()
+        {   
+            Foo<Axis.Y> f;
+            return foo(f);
+        }
+    `);
+    checkInt(program, callFunction(program, "testX", [], []), 0);
+    checkInt(program, callFunction(program, "testY", [], []), 1);
+}
+
 let filter = /.*/; // run everything by default
 if (this["arguments"]) {
     for (let i = 0; i < arguments.length; i++) {
index db32f2e..77a0566 100644 (file)
 "use strict";
 
 let UintLiteral = createLiteral({
+    literalClassName: "UintLiteral",
     preferredTypeName: "uint",
     
+    negConstexpr(origin)
+    {
+        return new UintLiteral(origin, (-this.value) >>> 0);
+    },
+    
     createType(origin, value)
     {
         return new UintLiteralType(origin, value);
index 4c85637..a406e07 100644 (file)
@@ -141,6 +141,22 @@ class Visitor {
         node.type.visit(this);
     }
     
+    visitEnumType(node)
+    {
+        node.baseType.visit(this);
+        for (let member of node.members)
+            member.visit(this);
+    }
+    
+    visitEnumMember(node)
+    {
+        Node.visit(node.value, this);
+    }
+    
+    visitEnumLiteral(node)
+    {
+    }
+    
     visitElementalType(node)
     {
         node.elementType.visit(this);