Figure out how WSL will support field overloads like float4.xz and friends
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Sep 2017 18:51:52 +0000 (18:51 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Sep 2017 18:51:52 +0000 (18:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177031

Reviewed by JF Bastien.

WSL needs to support getters and setters, so that we can do things like:

    float4 vec;
    vec.zx = float2(1, 2);
    // z = 1, x = 2

There's no way to express this float4.zx returning a pointer to something, since it's doing swizzling.
It could return a crazy smart pointer, but that sounds like a lot of work. So, I decided to go for
lvalue emulation instead.

The idea of lvalue emulation is that when we try to make an lvalue work for an assignment or RMW
operation (like += and friends), we consider the possibility that we have to first load the value using
a getter and then store it with a setter. This patch makes this work recursively, so that this will
work:

    float4 vec;
    vec.zwx.y = 42;
    // now w is 42

This works because we decompose it automatically:

    float4 vec;
    float3 tmp = vec.zwx;
    tmp.y = 42;
    vec.zwx = tmp;

This'll work to any depth.

To check if this works, this patch adds two substantial new tests called TEST_genericAccessors() and
TEST_bitSubscriptAccessor(). This tests that this stuff works with a lot of generic types, and that it's
possible to turn integers into bitvectors using subscript overloading. This patch also adds smaller unit
tests also.

Oh, and it's now possible to get an array's length.

* WebGPUShadingLanguageRI/AddressSpace.js:
(needsPrimitiveProtocol):
(protocolSuffix):
* WebGPUShadingLanguageRI/All.js:
* WebGPUShadingLanguageRI/AnonymousVariable.js: Added.
(AnonymousVariable):
(AnonymousVariable.prototype.get origin):
(AnonymousVariable.prototype.get name):
(AnonymousVariable.prototype.toString):
* WebGPUShadingLanguageRI/ArrayRefType.js:
(ArrayRefType.prototype.argumentForAndOverload):
(ArrayRefType.prototype.argumentTypeForAndOverload):
* WebGPUShadingLanguageRI/ArrayType.js:
(ArrayType.prototype.argumentForAndOverload):
(ArrayType.prototype.argumentTypeForAndOverload):
(ArrayType):
* WebGPUShadingLanguageRI/Assignment.js:
(Assignment):
* WebGPUShadingLanguageRI/CallAssignment.js: Removed.
* WebGPUShadingLanguageRI/CallExpression.js:
(CallExpression.resolve):
(CallExpression.prototype.resolve):
(CallExpression.prototype.resolveToOverload):
* WebGPUShadingLanguageRI/Checker.js:
(Checker.prototype.visitProgram):
(Checker.prototype.visitFuncDef):
(Checker.prototype.visitNativeFunc):
(Checker.prototype.visitProtocolDecl):
(Checker.prototype.visitIdentityExpression):
(Checker.prototype.visitReadModifyWriteExpression):
(Checker.prototype.visitAnonymousVariable):
(Checker.prototype.visitMakeArrayRefExpression):
(Checker.prototype._finishVisitingPropertyAccess):
(Checker.prototype.visitDotExpression):
(Checker.prototype.visitIndexExpression):
(Checker.prototype.visitCallExpression):
(Checker):
(Checker.prototype.visitProtocolDecl.NoticeTypeVariable.prototype.visitTypeRef): Deleted.
(Checker.prototype.visitProtocolDecl.NoticeTypeVariable.prototype.visitVariableRef): Deleted.
(Checker.prototype.visitProtocolDecl.NoticeTypeVariable): Deleted.
(Checker.prototype.visitProtocolDecl.set throw): Deleted.
* WebGPUShadingLanguageRI/CommaExpression.js:
* WebGPUShadingLanguageRI/DotExpression.js:
(DotExpression):
(DotExpression.prototype.get struct):
(DotExpression.prototype.get getFuncName):
(DotExpression.prototype.get andFuncName):
(DotExpression.prototype.get setFuncName):
* WebGPUShadingLanguageRI/EBuffer.js:
* WebGPUShadingLanguageRI/EBufferBuilder.js:
(EBufferBuilder.prototype.visitAnonymousVariable):
(EBufferBuilder.prototype.visitLetExpression): Deleted.
* WebGPUShadingLanguageRI/Evaluator.js:
(Evaluator.prototype.visitIdentityExpression):
(Evaluator.prototype.visitMakePtrExpression):
(Evaluator.prototype.visitAnonymousVariable):
(Evaluator.prototype.visitDotExpression): Deleted.
(Evaluator.prototype.visitLetExpression): Deleted.
* WebGPUShadingLanguageRI/ExpressionFinder.js:
(ExpressionFinder.prototype.visitReadModifyWriteExpression):
(ExpressionFinder.prototype.visitIdentityExpression):
* WebGPUShadingLanguageRI/ForLoop.js:
(ForLoop.prototype.toString):
(ForLoop):
* WebGPUShadingLanguageRI/Func.js:
(Func):
* WebGPUShadingLanguageRI/FuncInstantiator.js:
(FuncInstantiator.prototype.getUnique.InstantiationSubstitution.prototype.visitCallExpression):
(FuncInstantiator.prototype.getUnique.InstantiationSubstitution):
(FuncInstantiator.prototype.getUnique.Instantiate.prototype.visitFuncDef):
(FuncInstantiator.prototype.getUnique.Instantiate.prototype.visitNativeFunc):
(FuncInstantiator.prototype.getUnique.Instantiate):
(FuncInstantiator.prototype.getUnique):
(FuncInstantiator):
* WebGPUShadingLanguageRI/IdentityExpression.js: Added.
(IdentityExpression):
(IdentityExpression.prototype.get target):
(IdentityExpression.prototype.get unifyNode):
(IdentityExpression.prototype.get isConstexpr):
(IdentityExpression.prototype.get isLValue):
(IdentityExpression.prototype.get addressSpace):
(IdentityExpression.prototype.toString):
* WebGPUShadingLanguageRI/IndexExpression.js: Added.
(IndexExpression):
(IndexExpression.prototype.get array):
(IndexExpression.prototype.get index):
(IndexExpression.prototype.get isLValue):
(IndexExpression.prototype.get addressSpace):
(IndexExpression.prototype.get getFuncName):
(IndexExpression.prototype.get andFuncName):
(IndexExpression.prototype.get setFuncName):
(IndexExpression.prototype.toString):
* WebGPUShadingLanguageRI/InferTypesForCall.js:
(inferTypesForCall):
* WebGPUShadingLanguageRI/Inline.js:
(_inlineFunction):
* WebGPUShadingLanguageRI/Inliner.js:
(Inliner.prototype.visitCallExpression):
(Inliner.prototype.visitDotExpression): Deleted.
* WebGPUShadingLanguageRI/Intrinsics.js:
(Intrinsics):
* WebGPUShadingLanguageRI/LetExpression.js: Removed.
* WebGPUShadingLanguageRI/MakeArrayRefExpression.js:
(MakeArrayRefExpression.prototype.becomeConvertPtrToArrayRefExpression): Deleted.
* WebGPUShadingLanguageRI/NameContext.js:
(NameContext):
(NameContext.prototype.add):
(NameContext.prototype.doStatement):
(NameContext.prototype.handleDefining): Deleted.
(NameContext.prototype.isDefined): Deleted.
(NameContext.prototype.define): Deleted.
(NameContext.prototype.defineAll): Deleted.
* WebGPUShadingLanguageRI/NameResolver.js:
(NameResolver.prototype.doStatement):
(NameResolver.prototype.visitProtocolDecl):
(NameResolver.prototype.visitProgram): Deleted.
* WebGPUShadingLanguageRI/NativeFunc.js:
(NativeFunc):
* WebGPUShadingLanguageRI/NativeFuncInstance.js:
(NativeFuncInstance):
(NativeFuncInstance.prototype.get implementationData):
* WebGPUShadingLanguageRI/Node.js:
(Node.prototype.visit):
(Node.visit):
(Node.unify): Deleted.
* WebGPUShadingLanguageRI/NormalUsePropertyResolver.js: Added.
(NormalUsePropertyResolver.prototype.visitDotExpression):
(NormalUsePropertyResolver.prototype.visitIndexExpression):
(NormalUsePropertyResolver):
* WebGPUShadingLanguageRI/Parse.js:
(finishParsingPostIncrement):
(parsePossibleSuffix):
(finishParsingPreIncrement):
(genericParseCommaExpression):
(parseFuncName):
* WebGPUShadingLanguageRI/Prepare.js:
(prepare):
* WebGPUShadingLanguageRI/Program.js:
(Program):
(Program.prototype.get globalNameContext):
(Program.prototype.add):
* WebGPUShadingLanguageRI/PropertyAccessExpression.js: Added.
(PropertyAccessExpression):
(PropertyAccessExpression.prototype.get resultType):
(PropertyAccessExpression.prototype.rewriteAfterCloning):
(PropertyAccessExpression.prototype.updateCallsAfterChangingBase):
(PropertyAccessExpression.prototype.emitGet):
(PropertyAccessExpression.prototype.emitSet):
* WebGPUShadingLanguageRI/PropertyResolver.js: Added.
(PropertyResolver.prototype._visitPropertyAccess):
(PropertyResolver.prototype.visitDotExpression):
(PropertyResolver.prototype.visitIndexExpression):
(PropertyResolver.prototype._handleReadModifyWrite):
(PropertyResolver.prototype.visitReadModifyWriteExpression):
(PropertyResolver.prototype.visitAssignment):
(PropertyResolver.visitMakePtrExpression):
(PropertyResolver.prototype.visitMakeArrayRefExpression):
(PropertyResolver):
* WebGPUShadingLanguageRI/PtrType.js:
(PtrType.prototype.argumentForAndOverload):
(PtrType.prototype.argumentTypeForAndOverload):
(PtrType.prototype.returnTypeFromAndOverload):
* WebGPUShadingLanguageRI/ReadModifyWriteExpression.js: Added.
(ReadModifyWriteExpression):
(ReadModifyWriteExpression.prototype.get lValue):
(ReadModifyWriteExpression.prototype.oldValueRef):
(ReadModifyWriteExpression.prototype.newValueRef):
(ReadModifyWriteExpression.prototype.toString):
* WebGPUShadingLanguageRI/ResolveNames.js:
(createNameResolver):
(resolveNamesInTypes):
(resolveNamesInProtocols):
(resolveNamesInFunctions):
(resolveNames): Deleted.
* WebGPUShadingLanguageRI/ResolveOverloadImpl.js:
(resolveOverloadImpl):
* WebGPUShadingLanguageRI/ResolveProperties.js: Added.
(resolveProperties):
* WebGPUShadingLanguageRI/ResolveTypeDefs.js:
(resolveTypeDefsInTypes):
(resolveTypeDefsInProtocols):
(resolveTypeDefsInFunctions):
(resolveTypeDefs): Deleted.
* WebGPUShadingLanguageRI/ReturnChecker.js:
(ReturnChecker.prototype._isBoolCastFromLiteralTrue):
* WebGPUShadingLanguageRI/Rewriter.js:
(Rewriter):
(Rewriter.prototype.visitVariableDecl):
(Rewriter.prototype.visitTypeRef):
(Rewriter.prototype.visitAssignment):
(Rewriter.prototype.visitReadModifyWriteExpression):
(Rewriter.prototype.visitDereferenceExpression):
(Rewriter.prototype._handlePropertyAccessExpression):
(Rewriter.prototype.visitDotExpression):
(Rewriter.prototype.visitIndexExpression):
(Rewriter.prototype.visitReturn):
(Rewriter.prototype.visitGenericLiteralType):
(Rewriter.prototype.visitNullType):
(Rewriter.prototype.processDerivedCallData):
(Rewriter.prototype.visitCallExpression):
(Rewriter.prototype.visitFunctionLikeBlock):
(Rewriter.prototype.visitIfStatement):
(Rewriter.prototype.visitForLoop):
(Rewriter.prototype.visitAnonymousVariable):
(Rewriter.prototype.visitIdentityExpression):
(Rewriter.prototype.visitLetExpression): Deleted.
* WebGPUShadingLanguageRI/StandardLibrary.js:
* WebGPUShadingLanguageRI/StructLayoutBuilder.js:
(StructLayoutBuilder.prototype.visitNativeFuncInstance):
(StructLayoutBuilder):
* WebGPUShadingLanguageRI/StructType.js:
(StructType.prototype.instantiate):
* WebGPUShadingLanguageRI/Substitution.js:
(Substitution.prototype.get map):
* WebGPUShadingLanguageRI/SuffixCallAssignment.js: Removed.
* WebGPUShadingLanguageRI/SynthesizeStructAccessors.js: Added.
(synthesizeStructAccessors.createTypeParameters):
(synthesizeStructAccessors.):
(synthesizeStructAccessors.createFieldType):
(synthesizeStructAccessors.createTypeRef):
(synthesizeStructAccessors.setupAnder):
(synthesizeStructAccessors):
* WebGPUShadingLanguageRI/Test.html:
* WebGPUShadingLanguageRI/Test.js:
(checkNumber):
(TEST_simpleProtocol):
(TEST_assignLength):
(TEST_simpleSetter):
(TEST_genericAccessors):
* WebGPUShadingLanguageRI/Type.js:
(Type.prototype.get isRef):
(Type.prototype.argumentForAndOverload):
(Type.prototype.argumentTypeForAndOverload):
(Type.prototype.returnTypeFromAndOverload):
(Type):
* WebGPUShadingLanguageRI/TypeParameterRewriter.js: Added.
(TypeParameterRewriter.prototype.visitConstexprTypeParameter):
(TypeParameterRewriter.prototype.visitTypeVariable):
(TypeParameterRewriter):
* WebGPUShadingLanguageRI/TypeVariableTracker.js: Added.
(TypeVariableTracker):
* WebGPUShadingLanguageRI/Value.js:
(Value.prototype.become):
(Value):
* WebGPUShadingLanguageRI/VariableRef.js:
(VariableRef.prototype.get unifyNode):
(VariableRef.prototype.get addressSpace):
* WebGPUShadingLanguageRI/Visitor.js:
(Visitor.prototype.visitNativeFuncInstance):
(Visitor.prototype.visitProtocolDecl):
(Visitor): Deleted.
* WebGPUShadingLanguageRI/VisitorBase.js: Removed.

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

62 files changed:
Tools/ChangeLog
Tools/WebGPUShadingLanguageRI/AddressSpace.js
Tools/WebGPUShadingLanguageRI/All.js
Tools/WebGPUShadingLanguageRI/AnonymousVariable.js [moved from Tools/WebGPUShadingLanguageRI/LetExpression.js with 80% similarity]
Tools/WebGPUShadingLanguageRI/ArrayRefType.js
Tools/WebGPUShadingLanguageRI/ArrayType.js
Tools/WebGPUShadingLanguageRI/Assignment.js
Tools/WebGPUShadingLanguageRI/CallExpression.js
Tools/WebGPUShadingLanguageRI/Checker.js
Tools/WebGPUShadingLanguageRI/CommaExpression.js
Tools/WebGPUShadingLanguageRI/DereferenceExpression.js
Tools/WebGPUShadingLanguageRI/DotExpression.js
Tools/WebGPUShadingLanguageRI/EBuffer.js
Tools/WebGPUShadingLanguageRI/EBufferBuilder.js
Tools/WebGPUShadingLanguageRI/Evaluator.js
Tools/WebGPUShadingLanguageRI/ExpressionFinder.js
Tools/WebGPUShadingLanguageRI/FindHighZombies.js [moved from Tools/WebGPUShadingLanguageRI/VisitorBase.js with 93% similarity]
Tools/WebGPUShadingLanguageRI/ForLoop.js
Tools/WebGPUShadingLanguageRI/Func.js
Tools/WebGPUShadingLanguageRI/FuncInstantiator.js
Tools/WebGPUShadingLanguageRI/HighZombieFinder.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/IdentityExpression.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/IndexExpression.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/InferTypesForCall.js
Tools/WebGPUShadingLanguageRI/Inline.js
Tools/WebGPUShadingLanguageRI/Inliner.js
Tools/WebGPUShadingLanguageRI/Intrinsics.js
Tools/WebGPUShadingLanguageRI/Lexer.js
Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js
Tools/WebGPUShadingLanguageRI/NameContext.js
Tools/WebGPUShadingLanguageRI/NameResolver.js
Tools/WebGPUShadingLanguageRI/NativeFunc.js
Tools/WebGPUShadingLanguageRI/NativeFuncInstance.js
Tools/WebGPUShadingLanguageRI/Node.js
Tools/WebGPUShadingLanguageRI/NormalUsePropertyResolver.js [moved from Tools/WebGPUShadingLanguageRI/SuffixCallAssignment.js with 80% similarity]
Tools/WebGPUShadingLanguageRI/Parse.js
Tools/WebGPUShadingLanguageRI/Prepare.js
Tools/WebGPUShadingLanguageRI/Program.js
Tools/WebGPUShadingLanguageRI/PropertyAccessExpression.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/PropertyResolver.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/ProtocolDecl.js
Tools/WebGPUShadingLanguageRI/PtrType.js
Tools/WebGPUShadingLanguageRI/ReadModifyWriteExpression.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/ResolveNames.js
Tools/WebGPUShadingLanguageRI/ResolveOverloadImpl.js
Tools/WebGPUShadingLanguageRI/ResolveProperties.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/ResolveTypeDefs.js
Tools/WebGPUShadingLanguageRI/ReturnChecker.js
Tools/WebGPUShadingLanguageRI/Rewriter.js
Tools/WebGPUShadingLanguageRI/StandardLibrary.js
Tools/WebGPUShadingLanguageRI/StructLayoutBuilder.js
Tools/WebGPUShadingLanguageRI/StructType.js
Tools/WebGPUShadingLanguageRI/Substitution.js
Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/Test.html
Tools/WebGPUShadingLanguageRI/Test.js
Tools/WebGPUShadingLanguageRI/Type.js
Tools/WebGPUShadingLanguageRI/TypeParameterRewriter.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/TypeVariableTracker.js [moved from Tools/WebGPUShadingLanguageRI/CallAssignment.js with 76% similarity]
Tools/WebGPUShadingLanguageRI/Value.js
Tools/WebGPUShadingLanguageRI/VariableRef.js
Tools/WebGPUShadingLanguageRI/Visitor.js

index 7ca5183..86b3714 100644 (file)
@@ -1,3 +1,298 @@
+2017-09-16  Filip Pizlo  <fpizlo@apple.com>
+
+        Figure out how WSL will support field overloads like float4.xz and friends
+        https://bugs.webkit.org/show_bug.cgi?id=177031
+
+        Reviewed by JF Bastien.
+        
+        WSL needs to support getters and setters, so that we can do things like:
+        
+            float4 vec;
+            vec.zx = float2(1, 2);
+            // z = 1, x = 2
+        
+        There's no way to express this float4.zx returning a pointer to something, since it's doing swizzling.
+        It could return a crazy smart pointer, but that sounds like a lot of work. So, I decided to go for
+        lvalue emulation instead.
+        
+        The idea of lvalue emulation is that when we try to make an lvalue work for an assignment or RMW
+        operation (like += and friends), we consider the possibility that we have to first load the value using
+        a getter and then store it with a setter. This patch makes this work recursively, so that this will
+        work:
+        
+            float4 vec;
+            vec.zwx.y = 42;
+            // now w is 42
+        
+        This works because we decompose it automatically:
+        
+            float4 vec;
+            float3 tmp = vec.zwx;
+            tmp.y = 42;
+            vec.zwx = tmp;
+        
+        This'll work to any depth.
+        
+        To check if this works, this patch adds two substantial new tests called TEST_genericAccessors() and
+        TEST_bitSubscriptAccessor(). This tests that this stuff works with a lot of generic types, and that it's
+        possible to turn integers into bitvectors using subscript overloading. This patch also adds smaller unit
+        tests also.
+        
+        Oh, and it's now possible to get an array's length.
+
+        * WebGPUShadingLanguageRI/AddressSpace.js:
+        (needsPrimitiveProtocol):
+        (protocolSuffix):
+        * WebGPUShadingLanguageRI/All.js:
+        * WebGPUShadingLanguageRI/AnonymousVariable.js: Added.
+        (AnonymousVariable):
+        (AnonymousVariable.prototype.get origin):
+        (AnonymousVariable.prototype.get name):
+        (AnonymousVariable.prototype.toString):
+        * WebGPUShadingLanguageRI/ArrayRefType.js:
+        (ArrayRefType.prototype.argumentForAndOverload):
+        (ArrayRefType.prototype.argumentTypeForAndOverload):
+        * WebGPUShadingLanguageRI/ArrayType.js:
+        (ArrayType.prototype.argumentForAndOverload):
+        (ArrayType.prototype.argumentTypeForAndOverload):
+        (ArrayType):
+        * WebGPUShadingLanguageRI/Assignment.js:
+        (Assignment):
+        * WebGPUShadingLanguageRI/CallAssignment.js: Removed.
+        * WebGPUShadingLanguageRI/CallExpression.js:
+        (CallExpression.resolve):
+        (CallExpression.prototype.resolve):
+        (CallExpression.prototype.resolveToOverload):
+        * WebGPUShadingLanguageRI/Checker.js:
+        (Checker.prototype.visitProgram):
+        (Checker.prototype.visitFuncDef):
+        (Checker.prototype.visitNativeFunc):
+        (Checker.prototype.visitProtocolDecl):
+        (Checker.prototype.visitIdentityExpression):
+        (Checker.prototype.visitReadModifyWriteExpression):
+        (Checker.prototype.visitAnonymousVariable):
+        (Checker.prototype.visitMakeArrayRefExpression):
+        (Checker.prototype._finishVisitingPropertyAccess):
+        (Checker.prototype.visitDotExpression):
+        (Checker.prototype.visitIndexExpression):
+        (Checker.prototype.visitCallExpression):
+        (Checker):
+        (Checker.prototype.visitProtocolDecl.NoticeTypeVariable.prototype.visitTypeRef): Deleted.
+        (Checker.prototype.visitProtocolDecl.NoticeTypeVariable.prototype.visitVariableRef): Deleted.
+        (Checker.prototype.visitProtocolDecl.NoticeTypeVariable): Deleted.
+        (Checker.prototype.visitProtocolDecl.set throw): Deleted.
+        * WebGPUShadingLanguageRI/CommaExpression.js:
+        * WebGPUShadingLanguageRI/DotExpression.js:
+        (DotExpression):
+        (DotExpression.prototype.get struct):
+        (DotExpression.prototype.get getFuncName):
+        (DotExpression.prototype.get andFuncName):
+        (DotExpression.prototype.get setFuncName):
+        * WebGPUShadingLanguageRI/EBuffer.js:
+        * WebGPUShadingLanguageRI/EBufferBuilder.js:
+        (EBufferBuilder.prototype.visitAnonymousVariable):
+        (EBufferBuilder.prototype.visitLetExpression): Deleted.
+        * WebGPUShadingLanguageRI/Evaluator.js:
+        (Evaluator.prototype.visitIdentityExpression):
+        (Evaluator.prototype.visitMakePtrExpression):
+        (Evaluator.prototype.visitAnonymousVariable):
+        (Evaluator.prototype.visitDotExpression): Deleted.
+        (Evaluator.prototype.visitLetExpression): Deleted.
+        * WebGPUShadingLanguageRI/ExpressionFinder.js:
+        (ExpressionFinder.prototype.visitReadModifyWriteExpression):
+        (ExpressionFinder.prototype.visitIdentityExpression):
+        * WebGPUShadingLanguageRI/ForLoop.js:
+        (ForLoop.prototype.toString):
+        (ForLoop):
+        * WebGPUShadingLanguageRI/Func.js:
+        (Func):
+        * WebGPUShadingLanguageRI/FuncInstantiator.js:
+        (FuncInstantiator.prototype.getUnique.InstantiationSubstitution.prototype.visitCallExpression):
+        (FuncInstantiator.prototype.getUnique.InstantiationSubstitution):
+        (FuncInstantiator.prototype.getUnique.Instantiate.prototype.visitFuncDef):
+        (FuncInstantiator.prototype.getUnique.Instantiate.prototype.visitNativeFunc):
+        (FuncInstantiator.prototype.getUnique.Instantiate):
+        (FuncInstantiator.prototype.getUnique):
+        (FuncInstantiator):
+        * WebGPUShadingLanguageRI/IdentityExpression.js: Added.
+        (IdentityExpression):
+        (IdentityExpression.prototype.get target):
+        (IdentityExpression.prototype.get unifyNode):
+        (IdentityExpression.prototype.get isConstexpr):
+        (IdentityExpression.prototype.get isLValue):
+        (IdentityExpression.prototype.get addressSpace):
+        (IdentityExpression.prototype.toString):
+        * WebGPUShadingLanguageRI/IndexExpression.js: Added.
+        (IndexExpression):
+        (IndexExpression.prototype.get array):
+        (IndexExpression.prototype.get index):
+        (IndexExpression.prototype.get isLValue):
+        (IndexExpression.prototype.get addressSpace):
+        (IndexExpression.prototype.get getFuncName):
+        (IndexExpression.prototype.get andFuncName):
+        (IndexExpression.prototype.get setFuncName):
+        (IndexExpression.prototype.toString):
+        * WebGPUShadingLanguageRI/InferTypesForCall.js:
+        (inferTypesForCall):
+        * WebGPUShadingLanguageRI/Inline.js:
+        (_inlineFunction):
+        * WebGPUShadingLanguageRI/Inliner.js:
+        (Inliner.prototype.visitCallExpression):
+        (Inliner.prototype.visitDotExpression): Deleted.
+        * WebGPUShadingLanguageRI/Intrinsics.js:
+        (Intrinsics):
+        * WebGPUShadingLanguageRI/LetExpression.js: Removed.
+        * WebGPUShadingLanguageRI/MakeArrayRefExpression.js:
+        (MakeArrayRefExpression.prototype.becomeConvertPtrToArrayRefExpression): Deleted.
+        * WebGPUShadingLanguageRI/NameContext.js:
+        (NameContext):
+        (NameContext.prototype.add):
+        (NameContext.prototype.doStatement):
+        (NameContext.prototype.handleDefining): Deleted.
+        (NameContext.prototype.isDefined): Deleted.
+        (NameContext.prototype.define): Deleted.
+        (NameContext.prototype.defineAll): Deleted.
+        * WebGPUShadingLanguageRI/NameResolver.js:
+        (NameResolver.prototype.doStatement):
+        (NameResolver.prototype.visitProtocolDecl):
+        (NameResolver.prototype.visitProgram): Deleted.
+        * WebGPUShadingLanguageRI/NativeFunc.js:
+        (NativeFunc):
+        * WebGPUShadingLanguageRI/NativeFuncInstance.js:
+        (NativeFuncInstance):
+        (NativeFuncInstance.prototype.get implementationData):
+        * WebGPUShadingLanguageRI/Node.js:
+        (Node.prototype.visit):
+        (Node.visit):
+        (Node.unify): Deleted.
+        * WebGPUShadingLanguageRI/NormalUsePropertyResolver.js: Added.
+        (NormalUsePropertyResolver.prototype.visitDotExpression):
+        (NormalUsePropertyResolver.prototype.visitIndexExpression):
+        (NormalUsePropertyResolver):
+        * WebGPUShadingLanguageRI/Parse.js:
+        (finishParsingPostIncrement):
+        (parsePossibleSuffix):
+        (finishParsingPreIncrement):
+        (genericParseCommaExpression):
+        (parseFuncName):
+        * WebGPUShadingLanguageRI/Prepare.js:
+        (prepare):
+        * WebGPUShadingLanguageRI/Program.js:
+        (Program):
+        (Program.prototype.get globalNameContext):
+        (Program.prototype.add):
+        * WebGPUShadingLanguageRI/PropertyAccessExpression.js: Added.
+        (PropertyAccessExpression):
+        (PropertyAccessExpression.prototype.get resultType):
+        (PropertyAccessExpression.prototype.rewriteAfterCloning):
+        (PropertyAccessExpression.prototype.updateCallsAfterChangingBase):
+        (PropertyAccessExpression.prototype.emitGet):
+        (PropertyAccessExpression.prototype.emitSet):
+        * WebGPUShadingLanguageRI/PropertyResolver.js: Added.
+        (PropertyResolver.prototype._visitPropertyAccess):
+        (PropertyResolver.prototype.visitDotExpression):
+        (PropertyResolver.prototype.visitIndexExpression):
+        (PropertyResolver.prototype._handleReadModifyWrite):
+        (PropertyResolver.prototype.visitReadModifyWriteExpression):
+        (PropertyResolver.prototype.visitAssignment):
+        (PropertyResolver.visitMakePtrExpression):
+        (PropertyResolver.prototype.visitMakeArrayRefExpression):
+        (PropertyResolver):
+        * WebGPUShadingLanguageRI/PtrType.js:
+        (PtrType.prototype.argumentForAndOverload):
+        (PtrType.prototype.argumentTypeForAndOverload):
+        (PtrType.prototype.returnTypeFromAndOverload):
+        * WebGPUShadingLanguageRI/ReadModifyWriteExpression.js: Added.
+        (ReadModifyWriteExpression):
+        (ReadModifyWriteExpression.prototype.get lValue):
+        (ReadModifyWriteExpression.prototype.oldValueRef):
+        (ReadModifyWriteExpression.prototype.newValueRef):
+        (ReadModifyWriteExpression.prototype.toString):
+        * WebGPUShadingLanguageRI/ResolveNames.js:
+        (createNameResolver):
+        (resolveNamesInTypes):
+        (resolveNamesInProtocols):
+        (resolveNamesInFunctions):
+        (resolveNames): Deleted.
+        * WebGPUShadingLanguageRI/ResolveOverloadImpl.js:
+        (resolveOverloadImpl):
+        * WebGPUShadingLanguageRI/ResolveProperties.js: Added.
+        (resolveProperties):
+        * WebGPUShadingLanguageRI/ResolveTypeDefs.js:
+        (resolveTypeDefsInTypes):
+        (resolveTypeDefsInProtocols):
+        (resolveTypeDefsInFunctions):
+        (resolveTypeDefs): Deleted.
+        * WebGPUShadingLanguageRI/ReturnChecker.js:
+        (ReturnChecker.prototype._isBoolCastFromLiteralTrue):
+        * WebGPUShadingLanguageRI/Rewriter.js:
+        (Rewriter):
+        (Rewriter.prototype.visitVariableDecl):
+        (Rewriter.prototype.visitTypeRef):
+        (Rewriter.prototype.visitAssignment):
+        (Rewriter.prototype.visitReadModifyWriteExpression):
+        (Rewriter.prototype.visitDereferenceExpression):
+        (Rewriter.prototype._handlePropertyAccessExpression):
+        (Rewriter.prototype.visitDotExpression):
+        (Rewriter.prototype.visitIndexExpression):
+        (Rewriter.prototype.visitReturn):
+        (Rewriter.prototype.visitGenericLiteralType):
+        (Rewriter.prototype.visitNullType):
+        (Rewriter.prototype.processDerivedCallData):
+        (Rewriter.prototype.visitCallExpression):
+        (Rewriter.prototype.visitFunctionLikeBlock):
+        (Rewriter.prototype.visitIfStatement):
+        (Rewriter.prototype.visitForLoop):
+        (Rewriter.prototype.visitAnonymousVariable):
+        (Rewriter.prototype.visitIdentityExpression):
+        (Rewriter.prototype.visitLetExpression): Deleted.
+        * WebGPUShadingLanguageRI/StandardLibrary.js:
+        * WebGPUShadingLanguageRI/StructLayoutBuilder.js:
+        (StructLayoutBuilder.prototype.visitNativeFuncInstance):
+        (StructLayoutBuilder):
+        * WebGPUShadingLanguageRI/StructType.js:
+        (StructType.prototype.instantiate):
+        * WebGPUShadingLanguageRI/Substitution.js:
+        (Substitution.prototype.get map):
+        * WebGPUShadingLanguageRI/SuffixCallAssignment.js: Removed.
+        * WebGPUShadingLanguageRI/SynthesizeStructAccessors.js: Added.
+        (synthesizeStructAccessors.createTypeParameters):
+        (synthesizeStructAccessors.):
+        (synthesizeStructAccessors.createFieldType):
+        (synthesizeStructAccessors.createTypeRef):
+        (synthesizeStructAccessors.setupAnder):
+        (synthesizeStructAccessors):
+        * WebGPUShadingLanguageRI/Test.html:
+        * WebGPUShadingLanguageRI/Test.js:
+        (checkNumber):
+        (TEST_simpleProtocol):
+        (TEST_assignLength):
+        (TEST_simpleSetter):
+        (TEST_genericAccessors):
+        * WebGPUShadingLanguageRI/Type.js:
+        (Type.prototype.get isRef):
+        (Type.prototype.argumentForAndOverload):
+        (Type.prototype.argumentTypeForAndOverload):
+        (Type.prototype.returnTypeFromAndOverload):
+        (Type):
+        * WebGPUShadingLanguageRI/TypeParameterRewriter.js: Added.
+        (TypeParameterRewriter.prototype.visitConstexprTypeParameter):
+        (TypeParameterRewriter.prototype.visitTypeVariable):
+        (TypeParameterRewriter):
+        * WebGPUShadingLanguageRI/TypeVariableTracker.js: Added.
+        (TypeVariableTracker):
+        * WebGPUShadingLanguageRI/Value.js:
+        (Value.prototype.become):
+        (Value):
+        * WebGPUShadingLanguageRI/VariableRef.js:
+        (VariableRef.prototype.get unifyNode):
+        (VariableRef.prototype.get addressSpace):
+        * WebGPUShadingLanguageRI/Visitor.js:
+        (Visitor.prototype.visitNativeFuncInstance):
+        (Visitor.prototype.visitProtocolDecl):
+        (Visitor): Deleted.
+        * WebGPUShadingLanguageRI/VisitorBase.js: Removed.
+
 2017-09-18  Andy Estes  <aestes@apple.com>
 
         [Cocoa] Upstream WKSetCrashReportApplicationSpecificInformation() from WebKitSystemInterface
index d3ed26d..0fd45db 100644 (file)
@@ -39,6 +39,16 @@ function isAddressSpace(addressSpace)
     }
 }
 
+function needsPrimitiveProtocol(addressSpace)
+{
+    return addressSpace != "thread";
+}
+
+function protocolSuffix(addressSpace)
+{
+    return needsPrimitiveProtocol(addressSpace) ? ":primitive" : "";
+}
+
 function validateAddressSpace(addressSpace)
 {
     if (!isAddressSpace(addressSpace))
index 95bf1c8..e1a5efb 100644 (file)
@@ -29,13 +29,14 @@ load("Type.js");
 load("ReferenceType.js");
 load("Value.js");
 load("Expression.js");
-load("VisitorBase.js");
 load("Rewriter.js");
 load("Visitor.js");
 load("CreateLiteral.js");
 load("CreateLiteralType.js");
+load("PropertyAccessExpression.js");
 
 load("AddressSpace.js");
+load("AnonymousVariable.js");
 load("ArrayRefType.js");
 load("ArrayType.js");
 load("Assignment.js");
@@ -43,7 +44,6 @@ load("AutoWrapper.js");
 load("Block.js");
 load("BoolLiteral.js");
 load("Break.js");
-load("CallAssignment.js");
 load("CallExpression.js");
 load("CallFunction.js");
 load("Check.js");
@@ -71,6 +71,7 @@ load("EvaluationCommon.js");
 load("Evaluator.js");
 load("ExpressionFinder.js");
 load("Field.js");
+load("FindHighZombies.js");
 load("FlattenProtocolExtends.js");
 load("FloatLiteral.js");
 load("FloatLiteralType.js");
@@ -80,7 +81,10 @@ load("FuncDef.js");
 load("FuncInstantiator.js");
 load("FuncParameter.js");
 load("FunctionLikeBlock.js");
+load("HighZombieFinder.js");
+load("IdentityExpression.js");
 load("IfStatement.js");
+load("IndexExpression.js");
 load("InferTypesForCall.js");
 load("Inline.js");
 load("Inliner.js");
@@ -88,7 +92,6 @@ load("InstantiateImmediates.js");
 load("IntLiteral.js");
 load("IntLiteralType.js");
 load("Intrinsics.js");
-load("LetExpression.js");
 load("Lexer.js");
 load("LexerToken.js");
 load("LiteralTypeChecker.js");
@@ -103,20 +106,24 @@ load("NativeFunc.js");
 load("NativeFuncInstance.js");
 load("NativeType.js");
 load("NativeTypeInstance.js");
+load("NormalUsePropertyResolver.js");
 load("NullLiteral.js");
 load("NullType.js");
 load("OverloadResolutionFailure.js");
 load("Parse.js");
 load("Prepare.js");
 load("Program.js");
+load("PropertyResolver.js");
 load("Protocol.js");
 load("ProtocolDecl.js");
 load("ProtocolFuncDecl.js");
 load("ProtocolRef.js");
 load("PtrType.js");
+load("ReadModifyWriteExpression.js");
 load("RecursionChecker.js");
 load("ResolveNames.js");
 load("ResolveOverloadImpl.js");
+load("ResolveProperties.js");
 load("ResolveTypeDefs.js");
 load("Return.js");
 load("ReturnChecker.js");
@@ -125,12 +132,14 @@ load("StandardLibrary.js");
 load("StructLayoutBuilder.js");
 load("StructType.js");
 load("Substitution.js");
-load("SuffixCallAssignment.js");
+load("SynthesizeStructAccessors.js");
 load("TypeDef.js");
 load("TypeDefResolver.js");
 load("TypeOrVariableRef.js");
+load("TypeParameterRewriter.js");
 load("TypeRef.js");
 load("TypeVariable.js");
+load("TypeVariableTracker.js");
 load("TypedValue.js");
 load("UintLiteral.js");
 load("UintLiteralType.js");
  */
 "use strict";
 
-let letExpressionCount = 0;
+let anonymousVariableCount = 0;
 
-class LetExpression extends Value {
-    constructor(origin)
+class AnonymousVariable extends Value {
+    // You have to initialize the variable's value before use, but that could be quite late.
+    constructor(origin, type = null)
     {
         super();
         this._origin = origin;
-        this.index = letExpressionCount++;
-        this.argument = null;
-        this.body = null;
+        this.index = anonymousVariableCount++;
+        this.type = type;
     }
     
     get origin() { return this._origin; }
-    get name() { return "let<" + this.index + ">"; }
+    get name() { return "anonVar<" + this.index + ">"; }
     
     toString()
     {
-        return this.name + "(" + this.argument + ", " + this.body + ")";
+        return this.name;
     }
 }
 
index 7dd1873..9ec0000 100644 (file)
@@ -40,6 +40,15 @@ class ArrayRefType extends ReferenceType {
     
     get isArrayRef() { return true; }
 
+    argumentForAndOverload(origin, value)
+    {
+        return value;
+    }
+    argumentTypeForAndOverload(origin)
+    {
+        return this;
+    }
+
     toString()
     {
         return this.elementType + "[] " + this.addressSpace;
index 0975245..8388b91 100644 (file)
@@ -73,5 +73,16 @@ class ArrayType extends Type {
         
         return this.elementType.unify(unificationContext, other.elementType);
     }
+
+    argumentForAndOverload(origin, value)
+    {
+        let result = new MakeArrayRefExpression(origin, value);
+        result.numElements = this.numElements;
+        return result;
+    }
+    argumentTypeForAndOverload(origin)
+    {
+        return new ArrayRefType(origin, "thread", this.elementType);
+    }
 }
 
index 5609fb2..8184b15 100644 (file)
 "use strict";
 
 class Assignment extends Expression {
-    constructor(origin, lhs, rhs)
+    constructor(origin, lhs, rhs, type = null)
     {
         super(origin);
         this._lhs = lhs;
         this._rhs = rhs;
+        this.type = type;
     }
     
     get lhs() { return this._lhs; }
index a1776be..7af9260 100644 (file)
@@ -42,15 +42,82 @@ class CallExpression extends Expression {
     get isCast() { return this._isCast; }
     get returnType() { return this._returnType; }
     
-    resolve(overload)
+    static resolve(origin, possibleOverloads, typeParametersInScope, name, typeArguments, argumentList, argumentTypes, returnType)
+    {
+        let call = new CallExpression(origin, name, typeArguments, argumentList);
+        call.argumentTypes = argumentTypes;
+        if (returnType)
+            call.setCastData(returnType);
+        return {call, resultType: call.resolve(possibleOverloads, typeParametersInScope, typeArguments)};
+    }
+    
+    resolve(possibleOverloads, typeParametersInScope, typeArguments)
+    {
+        if (!possibleOverloads)
+            throw new WTypeError(this.origin.originString, "Did not find any functions named " + this.name);
+        
+        let overload = null;
+        let failures = [];
+        for (let typeParameter of typeParametersInScope) {
+            if (!(typeParameter instanceof TypeVariable))
+                continue;
+            if (!typeParameter.protocol)
+                continue;
+            let signatures =
+                typeParameter.protocol.protocolDecl.signaturesByNameWithTypeVariable(this.name, typeParameter);
+            if (!signatures)
+                continue;
+            overload = resolveOverloadImpl(signatures, this.typeArguments, this.argumentTypes, this.returnType);
+            if (overload.func)
+                break;
+            failures.push(...overload.failures);
+            overload = null;
+        }
+        if (!overload) {
+            overload = resolveOverloadImpl(
+                possibleOverloads, this.typeArguments, this.argumentTypes, this.returnType);
+            if (!overload.func) {
+                failures.push(...overload.failures);
+                let message = "Did not find function for call with ";
+                if (this.typeArguments.length)
+                    message += "type arguments <" + this.typeArguments + "> and ";
+                message += "argument types (" + this.argumentTypes + ")";
+                if (this.returnType)
+                    message +=" and return type " + this.returnType;
+                if (failures.length)
+                    message += ", but considered:\n" + failures.join("\n")
+                throw new WTypeError(this.origin.originString, message);
+            }
+        }
+        for (let i = 0; i < typeArguments.length; ++i) {
+            let typeArgumentType = typeArguments[i];
+            let typeParameter = overload.func.typeParameters[i];
+            if (!(typeParameter instanceof ConstexprTypeParameter))
+                continue;
+            if (!typeParameter.type.equalsWithCommit(typeArgumentType))
+                throw new Error("At " + this.origin.originString + " constexpr type argument and parameter types not equal: argument = " + typeArgumentType + ", parameter = " + typeParameter.type);
+        }
+        for (let i = 0; i < this.argumentTypes.length; ++i) {
+            let argumentType = this.argumentTypes[i];
+            let parameterType = overload.func.parameters[i].type.substituteToUnification(
+                overload.func.typeParameters, overload.unificationContext);
+            let result = argumentType.equalsWithCommit(parameterType);
+            if (!result)
+                throw new Error("At " + this.origin.originString + " argument and parameter types not equal after type argument substitution: argument = " + argumentType + ", parameter = " + parameterType);
+        }
+        return this.resolveToOverload(overload);
+    }
+    
+    resolveToOverload(overload)
     {
         this.func = overload.func;
         this.actualTypeArguments = overload.typeArguments.map(typeArgument => typeArgument instanceof Type ? typeArgument.visit(new AutoWrapper()) : typeArgument);
         let result = overload.func.returnType.substituteToUnification(
             overload.func.typeParameters, overload.unificationContext);
-        this.resultType = result.visit(new AutoWrapper());
         if (!result)
             throw new Error("Null return type");
+        result = result.visit(new AutoWrapper());
+        this.resultType = result;
         return result;
     }
     
index e2171ae..1a76bc6 100644 (file)
@@ -34,39 +34,47 @@ class Checker extends Visitor {
     
     visitProgram(node)
     {
-        for (let statement of node.topLevelStatements) {
+        let doStatement = statement => {
             this._currentStatement = statement;
             statement.visit(this);
         }
+        
+        for (let type of node.types.values())
+            doStatement(type);
+        for (let protocol of node.protocols.values())
+            doStatement(protocol);
+        for (let funcs of node.functions.values()) {
+            for (let func of funcs) {
+                this.visitFunc(func);
+            }
+        }
+        for (let funcs of node.functions.values()) {
+            for (let func of funcs)
+                doStatement(func);
+        }
+    }
+        
+    visitFuncDef(node)
+    {
+        node.body.visit(this);
+    }
+    
+    visitNativeFunc(node)
+    {
     }
     
     visitProtocolDecl(node)
     {
         for (let signature of node.signatures) {
-            let set = new Set();
-            function consider(thing)
-            {
-                if (thing.isUnifiable)
-                    set.add(thing);
-            }
-            class NoticeTypeVariable extends Visitor {
-                visitTypeRef(node)
-                {
-                    consider(node.type);
-                }
-                visitVariableRef(node)
-                {
-                    consider(node.variable);
-                }
-            }
-            let noticeTypeVariable = new NoticeTypeVariable();
+            let typeVariableTracker = new TypeVariableTracker();
             for (let parameterType of signature.parameterTypes)
-                parameterType.visit(noticeTypeVariable);
+                parameterType.visit(typeVariableTracker);
+            Node.visit(signature.returnTypeForOverloadResolution, typeVariableTracker);
             for (let typeParameter of signature.typeParameters) {
-                if (!set.has(typeParameter))
+                if (!typeVariableTracker.set.has(typeParameter))
                     throw WTypeError(typeParameter.origin.originString, "Type parameter to protocol signature not inferrable from value parameters");
             }
-            if (!set.has(node.typeVariable))
+            if (!typeVariableTracker.set.has(node.typeVariable))
                 throw new WTypeError(signature.origin.originString, "Protocol's type variable (" + node.name + ") not mentioned in signature: " + signature);
         }
     }
@@ -141,6 +149,32 @@ class Checker extends Visitor {
         return lhsType;
     }
     
+    visitIdentityExpression(node)
+    {
+        return node.target.visit(this);
+    }
+    
+    visitReadModifyWriteExpression(node)
+    {
+        if (!node.lValue.isLValue)
+            throw new WTypeError(node.origin.originString, "LHS of read-modify-write is not an LValue: " + node.lValue);
+        let lhsType = node.lValue.visit(this);
+        node.oldValueVar.type = lhsType;
+        node.newValueVar.type = lhsType;
+        node.oldValueVar.visit(this);
+        node.newValueVar.visit(this);
+        let newValueType = node.newValueExp.visit(this);
+        if (!lhsType.equalsWithCommit(newValueType))
+            return new WTypeError(node.origin.originString, "Type mismatch in read-modify-write: " + lhsType + " versus " + newValueType);
+        return node.resultExp.visit(this);
+    }
+    
+    visitAnonymousVariable(node)
+    {
+        if (!node.type)
+            throw new Error("Anonymous variable must know type before first appearance");
+    }
+    
     visitDereferenceExpression(node)
     {
         let type = node.ptr.visit(this).unifyNode;
@@ -148,6 +182,8 @@ class Checker extends Visitor {
             throw new WTypeError(node.origin.originString, "Type passed to dereference is not a pointer: " + type);
         node.type = type.elementType;
         node.addressSpace = type.addressSpace;
+        if (!node.addressSpace)
+            throw new Error("Null address space in type: " + type);
         return node.type;
     }
     
@@ -165,7 +201,7 @@ class Checker extends Visitor {
     {
         let elementType = node.lValue.visit(this).unifyNode;
         if (elementType instanceof PtrType) {
-            node.becomeConvertPtrToArrayRefExpression();
+            node.become(new ConvertPtrToArrayRefExpression(node.origin, node.lValue));
             return new ArrayRefType(node.origin, elementType.addressSpace, elementType.elementType);
         }
         
@@ -189,35 +225,87 @@ class Checker extends Visitor {
         throw new Error("Should not exist yet.");
     }
     
-    visitDotExpression(node)
+    _finishVisitingPropertyAccess(node, baseType, extraArgs, extraArgTypes)
     {
-        let structType = node.struct.visit(this).unifyNode;
+        baseType = baseType.visit(new AutoWrapper())
+        node.baseType = baseType;
+        
+        // Such a type must exist. This may throw if it doesn't.
+        let typeForAnd = baseType.argumentTypeForAndOverload(node.origin);
+        if (!typeForAnd)
+            throw new Error("Cannot get typeForAnd");
         
-        node.structType = structType.visit(new AutoWrapper());
+        let errorForGet;
+        let errorForAnd;
         
-        let underlyingStruct = structType;
+        try {
+            let result = CallExpression.resolve(
+                node.origin, node.possibleGetOverloads, this._currentStatement.typeParameters,
+                node.getFuncName, [], [node.base, ...extraArgs], [baseType, ...extraArgTypes], null);
+            node.callForGet = result.call;
+            node.resultTypeForGet = result.resultType;
+        } catch (e) {
+            if (!(e instanceof WTypeError))
+                throw e;
+            errorForGet = e;
+        }
         
-        if (structType instanceof TypeRef)
-            underlyingStruct = underlyingStruct.type;
+        try {
+            let baseForAnd = baseType.argumentForAndOverload(node.origin, node.base);
+            
+            let result = CallExpression.resolve(
+                node.origin, node.possibleAndOverloads, this._currentStatement.typeParameters,
+                node.andFuncName, [], [baseForAnd, ...extraArgs], [typeForAnd, ...extraArgTypes],
+                null);
+            node.callForAnd = result.call;
+            node.resultTypeForAnd = result.resultType.unifyNode.returnTypeFromAndOverload(node.origin);
+        } catch (e) {
+            if (!(e instanceof WTypeError))
+                throw e;
+            errorForAnd = e;
+        }
         
-        if (!(underlyingStruct instanceof StructType))
-            throw new WTypeError(node.origin.originString, "Operand to dot expression is not a struct type: " + structType);
+        if (!node.resultTypeForGet && !node.resultTypeForAnd) {
+            throw new WTypeError(
+                node.origin.originString,
+                "Cannot resolve access; tried by-value:\n" +
+                errorForGet.message + "\n" +
+                "and tried by-pointer:\n" +
+                errorForAnd.message);
+        }
         
-        if (structType instanceof TypeRef) 
-            underlyingStruct = underlyingStruct.instantiate(structType.typeArguments, "shallow");
+        if (node.resultTypeForGet && node.resultTypeForAnd
+            && !node.resultTypeForGet.equals(node.resultTypeForAnd))
+            throw new WTypeError(node.origin.originString, "Result type resolved by-value (" + node.resultTypeForGet + ") does not match result type resolved by-pointer (" + node.resultTypeForAnd + ")");
         
-        let field = underlyingStruct.fieldByName(node.fieldName);
-        if (!field)
-            throw new WTypeError(node.origin.originString, "Field " + node.fieldName + " not found in " + structType);
-        return field.type;
+        try {
+            let result = CallExpression.resolve(
+                node.origin, node.possibleSetOverloads, this._currentStatement.typeParameters,
+                node.setFuncName, [], [node.base, ...extraArgs, null], [baseType, ...extraArgTypes, node.resultType], null);
+            node.callForSet = result.call;
+            if (!result.resultType.equals(baseType))
+                throw new WTypeError(node.origin.originString, "Result type of setter " + result.call.func + " is not the base type " + baseType);
+        } catch (e) {
+            if (!(e instanceof WTypeError))
+                throw e;
+            node.errorForSet = e;
+        }
+        
+        return node.resultType;
+    }
+    
+    visitDotExpression(node)
+    {
+        let structType = node.struct.visit(this).unifyNode;
+        return this._finishVisitingPropertyAccess(node, structType, [], []);
     }
     
-    visitLetExpression(node)
+    visitIndexExpression(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);
+        let arrayType = node.array.visit(this).unifyNode;
+        let indexType = node.index.visit(this);
+        
+        return this._finishVisitingPropertyAccess(node, arrayType, [node.index], [indexType]);
     }
     
     visitVariableRef(node)
@@ -317,10 +405,10 @@ class Checker extends Visitor {
             result = expression.visit(this);
         return result;
     }
-
+    
     visitCallExpression(node)
     {
-        let typeArgumentTypes = node.typeArguments.map(typeArgument => typeArgument.visit(this));
+        let typeArguments = node.typeArguments.map(typeArgument => typeArgument.visit(this));
         let argumentTypes = node.argumentList.map(argument => {
             let newArgument = argument.visit(this);
             if (!newArgument)
@@ -328,77 +416,12 @@ class Checker extends Visitor {
             return newArgument.visit(new AutoWrapper());
         });
         
-        // Here we need to handle the cases where operator&[] is called with a type that isn't sufficiently
-        // referencey.
-        if (node.name == "operator&[]") {
-            let argType = argumentTypes[0].unifyNode;
-            if (argType instanceof PtrType)
-                throw new WTypeError(node.origin.originString, "Pointer subscript is not valid");
-            
-            if (argType instanceof ArrayType) {
-                node.argumentList[0] = new MakeArrayRefExpression(node.origin, node.argumentList[0]);
-                node.argumentList[0].numElements = argType.numElements;
-                argumentTypes[0] = new ArrayRefType(node.origin, "thread", argType.elementType);
-            } else if (!(argType instanceof ArrayRefType)) {
-                node.argumentList[0] = new MakePtrExpression(node.origin, node.argumentList[0]);
-                argumentTypes[0] = new PtrType(node.origin, "thread", argumentTypes[0]);
-            }
-        }
-        
         node.argumentTypes = argumentTypes;
         if (node.returnType)
             node.returnType.visit(this);
-        
-        let overload = null;
-        let failures = [];
-        for (let typeParameter of this._currentStatement.typeParameters) {
-            if (!(typeParameter instanceof TypeVariable))
-                continue;
-            if (!typeParameter.protocol)
-                continue;
-            let signatures =
-                typeParameter.protocol.protocolDecl.signaturesByNameWithTypeVariable(node.name, typeParameter);
-            if (!signatures)
-                continue;
-            overload = resolveOverloadImpl(signatures, node.typeArguments, argumentTypes, node.returnType);
-            if (overload.func)
-                break;
-            failures.push(...overload.failures);
-            overload = null;
-        }
-        if (!overload) {
-            overload = resolveOverloadImpl(
-                node.possibleOverloads, node.typeArguments, argumentTypes, node.returnType);
-            if (!overload.func) {
-                failures.push(...overload.failures);
-                let message = "Did not find function for call with ";
-                if (node.typeArguments.length)
-                    message += "type arguments <" + node.typeArguments + "> and ";
-                message += "argument types (" + argumentTypes + ")";
-                if (node.returnType)
-                    message +=" and return type " + node.returnType;
-                if (failures.length)
-                    message += ", but considered:\n" + failures.join("\n")
-                throw new WTypeError(node.origin.originString, message);
-            }
-        }
-        for (let i = 0; i < typeArgumentTypes.length; ++i) {
-            let typeArgumentType = typeArgumentTypes[i];
-            let typeParameter = overload.func.typeParameters[i];
-            if (!(typeParameter instanceof ConstexprTypeParameter))
-                continue;
-            if (!typeParameter.type.equalsWithCommit(typeArgumentType))
-                throw new Error("At " + node.origin.originString + " constexpr type argument and parameter types not equal: argument = " + typeArgumentType + ", parameter = " + typeParameter.type);
-        }
-        for (let i = 0; i < argumentTypes.length; ++i) {
-            let argumentType = argumentTypes[i];
-            let parameterType = overload.func.parameters[i].type.substituteToUnification(
-                overload.func.typeParameters, overload.unificationContext);
-            let result = argumentType.equalsWithCommit(parameterType);
-            if (!result)
-                throw new Error("At " + node.origin.originString + " argument and parameter types not equal after type argument substitution: argument = " + argumentType + ", parameter = " + parameterType);
-        }
-        return node.resolve(overload);
+
+        let result = node.resolve(node.possibleOverloads, this._currentStatement.typeParameters, typeArguments);
+        return result;
     }
 }
 
index 752d127..3f3ca36 100644 (file)
@@ -37,6 +37,9 @@ class CommaExpression extends Expression {
     
     get list() { return this._list; }
     
+    // NOTE: It's super tempting to say that CommaExpression is an lValue if its last entry is an lValue. But,
+    // PropertyResolver relies on this not being the case.
+    
     toString()
     {
         return "(" + this.list.toString() + ")";
index 3e072d5..b28b5db 100644 (file)
 "use strict";
 
 class DereferenceExpression extends Expression {
-    constructor(origin, ptr)
+    constructor(origin, ptr, type = null, addressSpace = null)
     {
         super(origin);
         this._ptr = ptr;
+        this.type = type;
+        this.addressSpace = addressSpace;
     }
     
     get ptr() { return this._ptr; }
index be3995d..c1fbade 100644 (file)
  */
 "use strict";
 
-class DotExpression extends Expression {
+class DotExpression extends PropertyAccessExpression {
     constructor(origin, struct, fieldName)
     {
-        super(origin);
-        this._struct = struct;
+        super(origin, struct);
         this._fieldName = fieldName;
     }
     
-    get struct() { return this._struct; }
+    get struct() { return this.base; }
     get fieldName() { return this._fieldName; }
     get isLValue() { return this.struct.isLValue; }
     get addressSpace() { return this.struct.addressSpace; }
     
+    get getFuncName() { return "operator." + this.fieldName; }
+    get andFuncName() { return "operator&." + this.fieldName; }
+    get setFuncName() { return "operator." + this.fieldName + "="; }
+    
     toString()
     {
         return "(" + this.struct + "." + this.fieldName + ")";
index b1cb124..1ef53e8 100644 (file)
@@ -60,12 +60,14 @@ class EBuffer {
     get(index)
     {
         if (index < 0 || index >= this._array.length)
-            throw new Error("out of bounds buffer access");
+            throw new Error("Out of bounds buffer access (buffer = " + this + ", index = " + index + ")");
         return this._array[index];
     }
     
     set(index, value)
     {
+        if (index < 0 || index >= this._array.length)
+            throw new Error("out of bounds buffer access (buffer = " + this + ", index = " + index + ")");
         this._array[index] = value;
     }
     
index c3bec74..1302643 100644 (file)
@@ -110,11 +110,9 @@ class EBufferBuilder extends Visitor {
         super.visitLogicalExpression(node);
     }
     
-    visitLetExpression(node)
+    visitAnonymousVariable(node)
     {
         this._createEPtrForNode(node);
-        node.argument.visit(this);
-        node.body.visit(this);
     }
     
     visitMakeArrayRefExpression(node)
index 1fe65df..a5805bd 100644 (file)
@@ -102,6 +102,11 @@ class Evaluator extends Visitor {
         return target;
     }
     
+    visitIdentityExpression(node)
+    {
+        return node.target.visit(this);
+    }
+    
     visitDereferenceExpression(node)
     {
         let ptr = node.ptr.visit(this).loadValue();
@@ -112,7 +117,8 @@ class Evaluator extends Visitor {
     
     visitMakePtrExpression(node)
     {
-        return node.ePtr.box(node.lValue.visit(this));
+        let ptr = node.lValue.visit(this);
+        return node.ePtr.box(ptr);
     }
     
     visitMakeArrayRefExpression(node)
@@ -125,12 +131,6 @@ class Evaluator extends Visitor {
         return node.ePtr.box(new EArrayRef(node.lValue.visit(this).loadValue(), 1));
     }
     
-    visitDotExpression(node)
-    {
-        let structPtr = node.struct.visit(this);
-        return structPtr.plus(node.field.offset);
-    }
-    
     visitCommaExpression(node)
     {
         let result;
@@ -249,10 +249,9 @@ class Evaluator extends Visitor {
         throw ContinueException;
     }
     
-    visitLetExpression(node)
+    visitAnonymousVariable(node)
     {
-        node.ePtr.copyFrom(node.argument.visit(this), node.type.size);
-        return node.body.visit(this);
+        node.type.populateDefaultValue(node.ePtr.buffer, node.ePtr.offset);
     }
     
     visitCallExpression(node)
@@ -265,6 +264,8 @@ class Evaluator extends Visitor {
             if (!type || !argument)
                 throw new Error("Cannot get type or argument; i = " + i + ", argument = " + argument + ", type = " + type + "; in " + node);
             let argumentValue = argument.visit(this);
+            if (!argumentValue)
+                throw new Error("Null argument value, i = " + i + ", node = " + node);
             callArguments.push(() => this._snapshot(type, null, argumentValue));
         }
         
index f60e6b1..30e6b03 100644 (file)
@@ -56,6 +56,16 @@ class ExpressionFinder extends Visitor {
         this._callback(node);
     }
     
+    visitReadModifyWriteExpression(node)
+    {
+        this._callback(node);
+    }
+    
+    visitIdentityExpression(node)
+    {
+        this._callback(node);
+    }
+    
     visitCallExpression(node)
     {
         this._callback(node);
@@ -24,9 +24,7 @@
  */
 "use strict";
 
-class VisitorBase {
-    constructor()
-    {
-        this._memoTable = new Map();
-    }
+function findHighZombies(program)
+{
+    program.visit(new HighZombieFinder());
 }
index 93c633b..ede2917 100644 (file)
@@ -43,7 +43,7 @@ class ForLoop extends Node {
 
     toString()
     {
-        return "for (" + (this.initialization ? this.initialization : " ") + "; " + (this.condition ? this.condition : "") + "; " + (this.increment ? this.increment : "") + ") " + body;
+        return "for (" + (this.initialization ? this.initialization : " ") + "; " + (this.condition ? this.condition : "") + "; " + (this.increment ? this.increment : "") + ") " + this.body;
     }
 };
 
index 69bb28c..3c12e03 100644 (file)
@@ -29,6 +29,12 @@ class Func extends Node {
     {
         if (!(origin instanceof LexerToken))
             throw new Error("Bad origin: " + origin);
+        for (let parameter of parameters) {
+            if (!parameter)
+                throw new Error("Null parameter");
+            if (!parameter.type)
+                throw new Error("Null parameter type");
+        }
         super();
         this._origin = origin;
         this._name = name;
index 6a4c739..7b2b255 100644 (file)
@@ -39,8 +39,14 @@ class FuncInstantiator {
     getUnique(func, typeArguments)
     {
         class FindTypeVariable extends Visitor {
+            visitTypeRef(node)
+            {
+                for (let typeArgument of node.typeArguments)
+                    typeArgument.visit(this);
+            }
+            
             visitTypeVariable(node) {
-                throw new Error("Unexpected type variable: " + node);
+                throw new Error("Unexpected type variable: " + node + " when instantiating " + func + " with arguments " + typeArguments);
             }
         }
         for (let typeArgument of typeArguments)
@@ -75,33 +81,38 @@ class FuncInstantiator {
                     let overload = resolveOverloadImpl(result.possibleOverloads, result.typeArguments, result.argumentTypes, result.returnTypeForOverloadResolution);
                     if (!overload.func)
                         throw new Error("Could not resolve protocol signature function call during instantiation: " + result.func + (overload.failures.length ? "; tried:\n" + overload.failures.join("\n") : ""));
-                    result.resolve(overload);
+                    result.resolveToOverload(overload);
                 }
                 
+                if (result.func.isNative)
+                    result.nativeFuncInstance = thisInstantiator.getUnique(result.func, result.actualTypeArguments);
+                
                 return result;
             }
         }
         
         let substitution = new InstantiationSubstitution(func.typeParameters, typeArguments);
+        let instantiateImmediates = new InstantiateImmediates();
         
-        class Instantiate extends VisitorBase {
+        class Instantiate {
             visitFuncDef(func)
             {
                 return new FuncDef(
                     func.origin, func.name,
-                    func.returnType.visit(substitution),
+                    func.returnType.visit(substitution).visit(instantiateImmediates),
                     [], // We're instantiated so we no longer take type parameters.
-                    func.parameters.map(parameter => parameter.visit(substitution)),
-                    func.body.visit(substitution));
+                    func.parameters.map(parameter => parameter.visit(substitution).visit(instantiateImmediates)),
+                    func.body.visit(substitution).visit(instantiateImmediates));
             }
             
             visitNativeFunc(func)
             {
                 return new NativeFuncInstance(
                     func,
-                    func.returnType.visit(substitution),
-                    func.parameters.map(parameter => parameter.visit(substitution)),
-                    func.isCast);
+                    func.returnType.visit(substitution).visit(instantiateImmediates),
+                    func.parameters.map(parameter => parameter.visit(substitution).visit(instantiateImmediates)),
+                    func.isCast,
+                    func.instantiateImplementation(substitution));
             }
         }
         let resultingFunc = func.visit(new Instantiate());
diff --git a/Tools/WebGPUShadingLanguageRI/HighZombieFinder.js b/Tools/WebGPUShadingLanguageRI/HighZombieFinder.js
new file mode 100644 (file)
index 0000000..caeee15
--- /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";
+
+// If a high-level construct somehow manages to live on when we're lowered, it's a high zombie.
+class HighZombieFinder extends Visitor {
+    _found(node)
+    {
+        throw new Error(node.origin.originString + ": High zombie: " + node);
+    }
+    
+    visitDotExpression(node)
+    {
+        this._found(node);
+    }
+    
+    visitIndexExpression(node)
+    {
+        this._found(node);
+    }
+    
+    visitCallExpression(node)
+    {
+        super.visitCallExpression(node);
+    }
+}
+
diff --git a/Tools/WebGPUShadingLanguageRI/IdentityExpression.js b/Tools/WebGPUShadingLanguageRI/IdentityExpression.js
new file mode 100644 (file)
index 0000000..f0ce6a3
--- /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 IdentityExpression extends Expression {
+    constructor(target)
+    {
+        super(target.origin);
+        this._target = target;
+    }
+    
+    get target() { return this._target; }
+    
+    get unifyNode() { return this.target.unifyNode; }
+    get isConstexpr() { return this.target.isConstexpr; }
+    get isLValue() { return this.target.isLValue; }
+    get addressSpace() { return this.target.addressSpace; }
+    
+    toString()
+    {
+        return "(" + this.target + ")";
+    }
+}
+
diff --git a/Tools/WebGPUShadingLanguageRI/IndexExpression.js b/Tools/WebGPUShadingLanguageRI/IndexExpression.js
new file mode 100644 (file)
index 0000000..61d23da
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 IndexExpression extends PropertyAccessExpression {
+    constructor(origin, array, index)
+    {
+        super(origin, array);
+        this._index = index;
+    }
+    
+    get array() { return this.base; }
+    get index() { return this._index; }
+    get isLValue() { return this.array.isLValue; }
+    get addressSpace() { return this.array.addressSpace; }
+    
+    get getFuncName() { return "operator[]"; }
+    get andFuncName() { return "operator&[]"; }
+    get setFuncName() { return "operator[]="; }
+
+    updateCalls()
+    {
+        if (this.callForGet)
+            this.callForGet.argumentList[1] = this.index;
+        if (this.callForAnd)
+            this.callForAnd.argumentList[1] = this.index;
+        if (this.callForSet)
+            this.callForSet.argumentList[1] = this.index;
+        super.updateCalls();
+    }
+
+    toString()
+    {
+        return "(" + this.array + "[" + this.index + "])";
+    }
+}
index 7b17996..446b351 100644 (file)
@@ -41,7 +41,7 @@ function inferTypesForCall(func, typeArguments, argumentTypes, returnType)
         if (!argumentTypes[i])
             throw new Error("Null argument type at i = " + i);
         if (!argumentTypes[i].unify(unificationContext, func.parameters[i].type))
-            return {failure: new OverloadResolutionFailure(func, "Argument #" + (i + 1) + " " + (func.parameters[i].name ? "for parameter " + func.parameters[i].name : "") + " does not match (passed " + argumentTypes[i] + ", require " + func.parameters[i].type + ")")};
+            return {failure: new OverloadResolutionFailure(func, "Argument #" + (i + 1) + " " + (func.parameters[i].name ? "for parameter " + func.parameters[i].name + " " : "") + "does not match (passed " + argumentTypes[i] + ", require " + func.parameters[i].type + ")")};
     }
     if (returnType && !returnType.unify(unificationContext, func.returnType))
         return {failure: new OverloadResolutionFailure(func, "Return type " + func.returnType + " does not match " + returnType)};
index fa4c87f..17e00e7 100644 (file)
@@ -43,8 +43,6 @@ function _inlineFunction(program, func, visiting)
     if (func.inlined || func.isNative)
         return;
     
-    func.rewrite(new InstantiateImmediates());
-    
     // This is the precise time when we can build EBuffers in order to get them to be uniqued by
     // type instantiation but nothing else.
     func.visit(new StructLayoutBuilder());
index 222b656..8b17277 100644 (file)
@@ -32,24 +32,15 @@ class Inliner extends Rewriter {
         this._visiting = visiting;
     }
     
-    visitDotExpression(node)
-    {
-        let result = super.visitDotExpression(node);
-        result.field = result.structType.unifyNode.fieldByName(result.fieldName);
-        if (result.field.offset == null)
-            throw new Error("Un-laid-out field: " + result.field + " (in " + result.structType + ")");
-        return result;
-    }
-    
     visitCallExpression(node)
     {
         let result = super.visitCallExpression(node);
+        if (result.nativeFuncInstance)
+            return result;
         return this._visiting.doVisit(node.func, () => {
             let func = this._program.funcInstantiator.getUnique(result.func, result.actualTypeArguments);
-            if (func.isNative) {
-                result.nativeFuncInstance = func;
-                return result;
-            }
+            if (func.isNative)
+                throw new Error("Unexpected native func: " + func);
             _inlineFunction(this._program, func, this._visiting);
             let resultingBlock = new FunctionLikeBlock(
                 result.origin, func.returnType, result.argumentList, func.parameters, func.body);
index f2d3dd2..caf5f60 100644 (file)
@@ -237,6 +237,88 @@ class Intrinsics {
             });
         
         this._map.set(
+            "native int operator&<>(int,int)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box(left.loadValue() & right.loadValue());
+            });
+        
+        this._map.set(
+            "native uint operator&<>(uint,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box((left.loadValue() & right.loadValue()) >>> 0);
+            });
+        
+        this._map.set(
+            "native int operator|<>(int,int)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box(left.loadValue() | right.loadValue());
+            });
+        
+        this._map.set(
+            "native uint operator|<>(uint,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box((left.loadValue() | right.loadValue()) >>> 0);
+            });
+        
+        this._map.set(
+            "native int operator^<>(int,int)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box(left.loadValue() ^ right.loadValue());
+            });
+        
+        this._map.set(
+            "native uint operator^<>(uint,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box((left.loadValue() ^ right.loadValue()) >>> 0);
+            });
+        
+        this._map.set(
+            "native int operator<<<>(int,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box(left.loadValue() << right.loadValue());
+            });
+        
+        this._map.set(
+            "native uint operator<<<>(uint,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box((left.loadValue() << right.loadValue()) >>> 0);
+            });
+        
+        this._map.set(
+            "native int operator>><>(int,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box(left.loadValue() >> right.loadValue());
+            });
+        
+        this._map.set(
+            "native uint operator>><>(uint,uint)",
+            func => {
+                func.implementation = ([left, right]) =>
+                    EPtr.box(left.loadValue() >>> right.loadValue());
+            });
+        
+        this._map.set(
+            "native int operator~<>(int)",
+            func => {
+                func.implementation = ([value]) => EPtr.box(~value.loadValue());
+            });
+        
+        this._map.set(
+            "native uint operator~<>(uint)",
+            func => {
+                func.implementation = ([value]) => EPtr.box((~value.loadValue()) >>> 0);
+            });
+        
+        this._map.set(
             "native float operator/<>(float,float)",
             func => {
                 func.implementation = ([left, right]) =>
@@ -397,30 +479,32 @@ class Intrinsics {
                     EPtr.box(left.loadValue() >= right.loadValue());
             });
         
-        let arrayElementPtr = func => {
-            func.implementation = ([ref, index], node) => {
-                ref = ref.loadValue();
-                if (!ref)
-                    throw new WTrapError(node.origin.originString, "Null dereference");
-                index = index.loadValue();
-                if (index > ref.length)
-                    throw new WTrapError(node.origin.originString, "Array index " + index + " is out of bounds of " + ref);
-                return EPtr.box(ref.ptr.plus(index * node.actualTypeArguments[0].size));
-            };
-        };
-        
-        this._map.set(
-            "native T^ thread operator&[]<T>(T[] thread,uint)",
-            arrayElementPtr);
-        this._map.set(
-            "native T^ threadgroup operator&[]<T:primitive>(T[] threadgroup,uint)",
-            arrayElementPtr);
-        this._map.set(
-            "native T^ device operator&[]<T:primitive>(T[] device,uint)",
-            arrayElementPtr);
-        this._map.set(
-            "native T^ constant operator&[]<T:primitive>(T[] constant,uint)",
-            arrayElementPtr);
+        for (let addressSpace of addressSpaces) {
+            this._map.set(
+                `native T^ ${addressSpace} operator&[]<T${protocolSuffix(addressSpace)}>(T[] ${addressSpace},uint)`,
+                func => {
+                    func.implementation = ([ref, index], node) => {
+                        ref = ref.loadValue();
+                        if (!ref)
+                            throw new WTrapError(node.origin.originString, "Null dereference");
+                        index = index.loadValue();
+                        if (index > ref.length)
+                            throw new WTrapError(node.origin.originString, "Array index " + index + " is out of bounds of " + ref);
+                        return EPtr.box(ref.ptr.plus(index * node.actualTypeArguments[0].size));
+                    };
+                });
+
+            this._map.set(
+                `native uint operator.length<T${protocolSuffix(addressSpace)}>(T[] ${addressSpace})`,
+                func => {
+                    func.implementation = ([ref], node) => {
+                        ref = ref.loadValue();
+                        if (!ref)
+                            return EPtr.box(0);
+                        return EPtr.box(ref.length);
+                    };
+                });
+        }
     }
     
     add(thing)
index 351d62a..09b17e5 100644 (file)
@@ -117,13 +117,19 @@ class Lexer {
             return result("identifier");
         }
 
+        if (/^0x[0-9a-fA-F]+u/.test(relevantText))
+            return result("uintHexLiteral");
+
+        if (/^0x[0-9a-fA-F]+/.test(relevantText))
+            return result("intHexLiteral");
+
         if (/^[0-9]+u/.test(relevantText))
             return result("uintLiteral");
 
         if (/^[0-9]+/.test(relevantText))
             return result("intLiteral");
         
-        if (/^->|>=|<=|==|!=|\+=|-=|\*=|\/=|%=|^=|\|=|&=|\+\+|--|&&|\|\||([{}()\[\]?:=+*\/,.%!~^&|<>@;-])/.test(relevantText))
+        if (/^<<|>>|->|>=|<=|==|!=|\+=|-=|\*=|\/=|%=|\^=|\|=|&=|\+\+|--|&&|\|\||([{}()\[\]?:=+*\/,.%!~^&|<>@;-])/.test(relevantText))
             return result("punctuation");
         
         let remaining = relevantText.substring(0, 20).split(/\s/)[0];
index 7fe6419..c061876 100644 (file)
@@ -33,11 +33,6 @@ class MakeArrayRefExpression extends Expression {
     
     get lValue() { return this._lValue; }
     
-    becomeConvertPtrToArrayRefExpression()
-    {
-        this.__proto__ = ConvertPtrToArrayRefExpression.prototype;
-    }
-    
     toString()
     {
         return "@" + (this.numElements ? "<<" + this.numElements + ">>" : "") + "(" + this.lValue + ")";
index 6137961..edc28b1 100644 (file)
@@ -36,7 +36,6 @@ class NameContext {
     {
         this._map = new Map();
         this._set = new Set();
-        this._defined = null;
         this._currentStatement = null;
         this._delegate = delegate;
         this._intrinsics = null;
@@ -62,7 +61,7 @@ class NameContext {
         if (!thing.name)
             return;
         
-        if (thing.isNative) {
+        if (thing.isNative && !thing.implementation) {
             if (!this._intrinsics)
                 throw new Error("Native function in a scope that does not recognize intrinsics");
             this._intrinsics.add(thing);
@@ -115,36 +114,11 @@ class NameContext {
         return null;
     }
     
-    handleDefining()
-    {
-        this._defined = new Set();
-    }
-    
-    isDefined(thing)
-    {
-        if (this._set.has(thing)) {
-            return !this._defined
-                || this._defined.has(thing);
-        }
-        return this._delegate && this._delegate.isDefined(thing);
-    }
-    
-    define(thing)
-    {
-        this._defined.add(thing);
-    }
-    
-    defineAll()
-    {
-        this._defined = null;
-    }
-    
     doStatement(statement, callback)
     {
         this._currentStatement = statement;
         callback();
         this._currentStatement = null;
-        this.define(statement);
     }
     
     recognizeIntrinsics()
index be504ea..ba2102d 100644 (file)
 // more convenient to check here than in the Checker. This usually involves things that need to be
 // checked before TypeRefToTypeDefSkipper.
 class NameResolver extends Visitor {
-    // It's totally OK to instantiate this with zero arguments (i.e. passing undefined for both of
-    // them) if you're creating a NameResolver to visit a Program.
     constructor(nameContext)
     {
         super();
         this._nameContext = nameContext;
     }
-    
-    visitProgram(node)
+
+    doStatement(statement)
     {
-        let nameContext = new NameContext(this._nameContext);
-        nameContext.program = node;
-        nameContext.recognizeIntrinsics();
-        nameContext.handleDefining();
-        node.intrinsics = nameContext.intrinsics;
-        for (let statement of node.topLevelStatements)
-            nameContext.add(statement);
-        let checker = new NameResolver(nameContext);
-        for (let statement of node.topLevelStatements)
-            nameContext.doStatement(statement, () => statement.visit(checker));
-        node.globalNameContext = nameContext;
+        this._nameContext.doStatement(statement, () => statement.visit(this));
     }
     
     _visitTypeParametersAndBuildNameContext(node)
@@ -207,8 +195,6 @@ class NameResolver extends Visitor {
             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;
         }
         
@@ -230,7 +216,6 @@ class NameResolver extends Visitor {
     visitReferenceType(node)
     {
         let nameContext = new NameContext(this._nameContext);
-        nameContext.defineAll();
         node.elementType.visit(new NameResolver(nameContext));
     }
     
@@ -258,6 +243,28 @@ class NameResolver extends Visitor {
         super.visitReturn(node);
     }
     
+    _handlePropertyAccess(node)
+    {
+        node.possibleGetOverloads = this._nameContext.get(Func, node.getFuncName);
+        node.possibleSetOverloads = this._nameContext.get(Func, node.setFuncName);
+        node.possibleAndOverloads = this._nameContext.get(Func, node.andFuncName);
+
+        if (!node.possibleGetOverloads && !node.possibleAndOverloads)
+            throw new WTypeError(node.origin.originString, "Cannot find either " + node.getFuncName + " or " + node.andFuncName);
+    }
+    
+    visitDotExpression(node)
+    {
+        this._handlePropertyAccess(node);
+        super.visitDotExpression(node);
+    }
+    
+    visitIndexExpression(node)
+    {
+        this._handlePropertyAccess(node);
+        super.visitIndexExpression(node);
+    }
+    
     visitCallExpression(node)
     {
         this._resolveTypeArguments(node.typeArguments);
index 747aa3a..3b80630 100644 (file)
@@ -29,6 +29,10 @@ class NativeFunc extends Func {
     {
         super(origin, name, returnType, typeParameters, parameters, isCast);
         this.isRestricted = false;
+        this.implementation = null;
+        this.instantiateImplementation = (substitution) => {};
+        this.visitImplementationData = (implementationData, visitor) => null;
+        this.didLayoutStructsInImplementationData = implementationData => null;
     }
     
     get isNative() { return true; }
index 36695a5..ef801c0 100644 (file)
 "use strict";
 
 class NativeFuncInstance extends Func {
-    constructor(func, returnType, parameters, isCast)
+    constructor(func, returnType, parameters, isCast, implementationData)
     {
         super(func.origin, func.name, returnType, [], parameters, isCast);
         this._func = func;
+        this._implementationData = implementationData;
     }
     
     get func() { return this._func; }
     get isNative() { return true; }
+    get implementationData() { return this._implementationData; }
 
     toDeclString()
     {
index 3f900f1..2f00e23 100644 (file)
@@ -33,9 +33,17 @@ class Node {
         let returnValue = visitFunc.call(visitor, this);
         if ("returnValue" in visitor)
             returnValue = visitor.returnValue;
+        
         return returnValue;
     }
     
+    static visit(node, visitor)
+    {
+        if (node instanceof Node)
+            return node.visit(visitor);
+        return node;
+    }
+    
     unify(unificationContext, other)
     {
         if (!other)
  */
 "use strict";
 
-class SuffixCallAssignment extends Expression {
-    constructor(origin, name, lhs)
+class NormalUsePropertyResolver extends Rewriter {
+    visitDotExpression(node)
     {
-        super(origin);
-        this._name = name;
-        this._lhs = lhs;
-        this.func = null;
+        return super.visitDotExpression(node).rewriteAfterCloning();
     }
     
-    get name() { return this._name; }
-    get lhs() { return this._lhs; }
-    
-    toString()
+    visitIndexExpression(node)
     {
-        return this.lhs + " suffix " + this.name;
+        return super.visitIndexExpression(node).rewriteAfterCloning();
     }
 }
 
index dbfa6b3..b6e46b4 100644 (file)
@@ -172,17 +172,37 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         if (token = tryConsumeKind("identifier"))
             return new VariableRef(token, token.text);
         if (token = tryConsumeKind("intLiteral")) {
-            let intVersion = Math.floor(+token.text);
+            let intVersion = (+token.text) | 0;
             if ("" + intVersion !== token.text)
-                lexer.fail("Integer literal is not an integer");
+                lexer.fail("Integer literal is not an integer: " + token.text);
             return new IntLiteral(token, intVersion);
         }
         if (token = tryConsumeKind("uintLiteral")) {
             let uintVersion = token.text.substr(0, token.text.length - 1) >>> 0;
             if (uintVersion + "u" !== token.text)
-                lexer.fail("Integer literal is not 32-bit unsigned integer");
+                lexer.fail("Integer literal is not 32-bit unsigned integer: " + token.text);
             return new UintLiteral(token, uintVersion);
         }
+        if ((token = tryConsumeKind("intHexLiteral"))
+            || (token = tryConsumeKind("uintHexLiteral"))) {
+            let hexString = token.text.substr(2);
+            if (token.kind == "uintHexLiteral")
+                hexString = hexString.substr(0, hexString.length - 1);
+            if (!hexString.length)
+                throw new Error("Bad hex literal: " + token);
+            let intVersion = parseInt(hexString, 16);
+            if (token.kind == "intHexLiteral")
+                intVersion = intVersion | 0;
+            else
+                intVersion = intVersion >>> 0;
+            if (intVersion.toString(16) !== hexString)
+                lexer.fail("Hex integer literal is not an integer: " + token.text);
+            if (token.kind == "intHexLiteral")
+                return new IntLiteral(token, intVersion);
+            return new UintLiteral(token, intVersion >>> 0);
+        }
+        if (token = tryConsumeKind("doubleLiteral"))
+            return new DoubleLiteral(token, +token.text);
         if (token = tryConsumeKind("floatLiteral")) {
             let text = token.text;
             let d = token.text.endsWith("d");
@@ -331,12 +351,14 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         consume("(");
         let argumentList = [];
         while (!test(")")) {
-            argumentList.push(parseExpression());
+            let argument = parsePossibleAssignment();
+            argumentList.push(argument);
             if (!tryConsume(","))
                 break;
         }
         consume(")");
-        return new CallExpression(name, name.text, typeArguments, argumentList);
+        let result = new CallExpression(name, name.text, typeArguments, argumentList);
+        return result;
     }
     
     function isCallExpression()
@@ -348,9 +370,9 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         });
     }
     
-    function emitIncrement(token, ptr, extraArg)
+    function emitIncrement(token, old, extraArg)
     {
-        let args = [new DereferenceExpression(token, VariableRef.wrap(ptr))];
+        let args = [old];
         if (extraArg)
             args.push(extraArg);
         
@@ -361,27 +383,15 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         if (name == "operator")
             throw new Error("Invalid name: " + name);
         
-        return new Assignment(
-            token,
-            new DereferenceExpression(token, VariableRef.wrap(ptr)),
-            new CallExpression(token, name, [], args));
+        return 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));
-        
-        ptr.body = oldValue;
-        
-        oldValue.body = new CommaExpression(token, [
-            emitIncrement(token, ptr),
-            VariableRef.wrap(oldValue)
-        ]);
-        return ptr;
+        let readModifyWrite = new ReadModifyWriteExpression(token, left);
+        readModifyWrite.newValueExp = emitIncrement(token, readModifyWrite.oldValueRef());
+        readModifyWrite.resultExp = readModifyWrite.oldValueRef();
+        return readModifyWrite;
     }
     
     function parsePossibleSuffix()
@@ -406,9 +416,7 @@ function parse(program, origin, originKind, lineNumberOffset, text)
             case "[": {
                 let index = parseExpression();
                 consume("]");
-                left = new DereferenceExpression(
-                    token,
-                    new CallExpression(token, "operator&[]", [], [left, index]));
+                left = new IndexExpression(token, left, index);
                 break;
             }
             default:
@@ -420,13 +428,10 @@ function parse(program, origin, originKind, lineNumberOffset, text)
     
     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;
+        let readModifyWrite = new ReadModifyWriteExpression(token, left);
+        readModifyWrite.newValueExp = emitIncrement(token, readModifyWrite.oldValueRef(), extraArg);
+        readModifyWrite.resultExp = readModifyWrite.newValueRef();
+        return readModifyWrite;
     }
     
     function parsePreIncrement()
@@ -586,6 +591,10 @@ function parse(program, origin, originKind, lineNumberOffset, text)
             }
             list.push(effectfulExpression);
         }
+        if (!list.length)
+            throw new Error("Length should never be zero");
+        if (list.length == 1)
+            return list[0];
         return new CommaExpression(origin, list);
     }
     
@@ -772,15 +781,30 @@ function parse(program, origin, originKind, lineNumberOffset, text)
     function parseFuncName()
     {
         if (tryConsume("operator")) {
-            let token = tryConsume("+", "-", "*", "/", "%", "^", "&", "|", "<", ">", "<=", ">=", "==", "++", "--", "&");
-            if (token) {
-                if (token.text != "&" || !tryConsume("["))
-                    return "operator" + token.text;
+            let token = consume("+", "-", "*", "/", "%", "^", "&", "|", "<", ">", "<=", ">=", "==", "++", "--", ".", "~", "<<", ">>", "[");
+            if (token.text == "&") {
+                if (tryConsume("[")) {
+                    consume("]");
+                    return "operator&[]";
+                }
+                if (tryConsume("."))
+                    return "operator&." + consumeKind("identifier").text;
+                return "operator&";
+            }
+            if (token.text == ".") {
+                let result = "operator." + consumeKind("identifier").text;
+                if (tryConsume("="))
+                    result += "=";
+                return result;
+            }
+            if (token.text == "[") {
                 consume("]");
-                return "operator&[]";
+                let result = "operator[]";
+                if (tryConsume("="))
+                    result += "=";
+                return result;
             }
-            let name = consumeKind("identifier");
-            return "operator " + name;
+            return "operator" + token.text;
         }
         return consumeKind("identifier").text;
     }
index d3537d4..e0b8ed7 100644 (file)
 function prepare(origin, lineNumberOffset, text)
 {
     let program = new Program();
+    
     parse(program, "/internal/stdlib", "native", 27, standardLibrary);
     parse(program, origin, "user", lineNumberOffset, text);
-    resolveNames(program);
-    resolveTypeDefs(program);
+    
+    let nameResolver = createNameResolver(program);
+    resolveNamesInTypes(program, nameResolver);
+    resolveNamesInProtocols(program, nameResolver);
+    resolveTypeDefsInTypes(program);
+    resolveTypeDefsInProtocols(program);
+    // FIXME: Need to verify that structre are not cyclic.
+    // https://bugs.webkit.org/show_bug.cgi?id=177044
+    synthesizeStructAccessors(program);
+    resolveNamesInFunctions(program, nameResolver);
+    resolveTypeDefsInFunctions(program);
+    
     flattenProtocolExtends(program);
     check(program);
     checkLiteralTypes(program);
+    resolveProperties(program);
+    findHighZombies(program);
+    checkLiteralTypes(program);
     checkProgramWrapped(program);
     checkReturns(program);
     checkUnreachableCode(program);
     checkLoops(program);
     checkRecursion(program);
     checkProgramWrapped(program);
+    findHighZombies(program);
     inline(program);
+    
     return program;
 }
 
index 4e6ca6a..bbc4b24 100644 (file)
@@ -33,6 +33,10 @@ class Program extends Node {
         this._types = new Map();
         this._protocols = new Map();
         this._funcInstantiator = new FuncInstantiator(this);
+        this._globalNameContext = new NameContext();
+        this._globalNameContext.program = this;
+        this._globalNameContext.recognizeIntrinsics();
+        this.intrinsics = this._globalNameContext.intrinsics;
     }
     
     get topLevelStatements() { return this._topLevelStatements; }
@@ -40,6 +44,7 @@ class Program extends Node {
     get types() { return this._types; }
     get protocols() { return this._protocols; }
     get funcInstantiator() { return this._funcInstantiator; }
+    get globalNameContext() { return this._globalNameContext; }
     
     add(statement)
     {
@@ -52,10 +57,11 @@ class Program extends Node {
         } else if (statement instanceof Type)
             this._types.set(statement.name, statement);
         else if (statement instanceof Protocol)
-            this._protocols.set(statement.add, statement);
+            this._protocols.set(statement.name, statement);
         else
             throw new Error("Statement is not a function or type: " + statement);
         this._topLevelStatements.push(statement);
+        this._globalNameContext.add(statement);
     }
     
     toString()
diff --git a/Tools/WebGPUShadingLanguageRI/PropertyAccessExpression.js b/Tools/WebGPUShadingLanguageRI/PropertyAccessExpression.js
new file mode 100644 (file)
index 0000000..09e426b
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 PropertyAccessExpression extends Expression {
+    constructor(origin, base)
+    {
+        super(origin);
+        this.base = base;
+    }
+    
+    get resultType() { return this.resultTypeForGet ? this.resultTypeForGet : this.resultTypeForAnd; }
+    
+    rewriteAfterCloning()
+    {
+        // At this point, node.base.isLValue will only return true if it's possible to get a pointer to it,
+        // since we will no longer see any DotExpressions or IndexExpressions. Also, node is a newly created
+        // node and everything beneath it is newly created, so we can edit it in-place.
+        
+        if ((this.base.isLValue || this.baseType.isRef) && this.callForAnd)
+            return new DereferenceExpression(this.origin, this.callForAnd, this.resultType, this.callForAnd.resultType.addressSpace);
+        
+        if (this.callForGet)
+            return this.callForGet;
+        
+        if (!this.callForAnd)
+            throw new Error(this.origin.originString + ": Property access without getter and ander: " + this);
+        
+        let anonVar = new AnonymousVariable(this.origin, this.baseType);
+        this.callForAnd.argumentList[0] = this.baseType.unifyNode.argumentForAndOverload(this.origin, VariableRef.wrap(anonVar));
+        return new CommaExpression(this.origin, [
+            anonVar,
+            new Assignment(this.origin, VariableRef.wrap(anonVar), this.base, this.baseType),
+            new DereferenceExpression(this.origin, this.callForAnd, this.resultType, this.callForAnd.resultType.addressSpace)
+        ]);
+    }
+    
+    updateCalls()
+    {
+        if (this.callForGet)
+            this.callForGet.argumentList[0] = this.base;
+        if (this.callForAnd)
+            this.callForAnd.argumentList[0] = this.baseType.argumentForAndOverload(this.origin, this.base);
+        if (this.callForSet)
+            this.callForSet.argumentList[0] = this.base;
+    }
+    
+    emitGet(base)
+    {
+        let result = this.visit(new Rewriter());
+        result.base = base;
+        result.updateCalls();
+        return result.rewriteAfterCloning();
+    }
+    
+    emitSet(base, value)
+    {
+        let result = this.visit(new Rewriter());
+        if (!result.callForSet)
+            throw new WTypeError(this.origin.originString, "Cannot emit set because: " + this.errorForSet.typeErrorMessage);
+        result.base = base;
+        result.updateCalls();
+        result.callForSet.argumentList[result.callForSet.argumentList.length - 1] = value;
+        return result.callForSet;
+    }
+}
+
diff --git a/Tools/WebGPUShadingLanguageRI/PropertyResolver.js b/Tools/WebGPUShadingLanguageRI/PropertyResolver.js
new file mode 100644 (file)
index 0000000..6f80800
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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 PropertyResolver extends Visitor {
+    _visitRValuesWithinLValue(node)
+    {
+        let visit = node => node.visit(this);
+        
+        class RValueFinder {
+            visitDotExpression(node)
+            {
+                node.struct.visit(this);
+            }
+            
+            visitIndexExpression(node)
+            {
+                node.array.visit(this);
+                visit(node.index);
+            }
+            
+            visitVariableRef(node)
+            {
+            }
+            
+            visitDereferenceExpression(node)
+            {
+                visit(node.ptr);
+            }
+            
+            visitIdentityExpression(node)
+            {
+                node.target.visit(this);
+            }
+        }
+        
+        node.visit(new RValueFinder());
+    }
+    
+    _visitPropertyAccess(node)
+    {
+        let newNode = node.visit(new NormalUsePropertyResolver());
+        newNode.visit(this);
+        node.become(newNode);
+    }
+    
+    visitDotExpression(node)
+    {
+        this._visitPropertyAccess(node);
+    }
+    
+    visitIndexExpression(node)
+    {
+        this._visitPropertyAccess(node);
+    }
+    
+    _handleReadModifyWrite(node)
+    {
+        let type = node.oldValueVar.type;
+        if (!type)
+            throw new Error("Null type");
+        let simpleLHS = node.lValue.visit(new NormalUsePropertyResolver());
+        if (simpleLHS.isLValue) {
+            if (!simpleLHS.addressSpace)
+                throw new Error(node.origin.originString + ": LHS without address space: " + simpleLHS);
+            let ptrType = new PtrType(node.origin, simpleLHS.addressSpace, type);
+            let ptrVar = new AnonymousVariable(node.origin, ptrType);
+            node.become(new CommaExpression(node.origin, [
+                node.oldValueVar, node.newValueVar, ptrVar,
+                new Assignment(
+                    node.origin, VariableRef.wrap(ptrVar),
+                    new MakePtrExpression(node.origin, simpleLHS), ptrType),
+                new Assignment(
+                    node.origin, node.oldValueRef(),
+                    new DereferenceExpression(
+                        node.origin, VariableRef.wrap(ptrVar), type, simpleLHS.addressSpace),
+                    type),
+                new Assignment(node.origin, node.newValueRef(), node.newValueExp, type),
+                new Assignment(
+                    node.origin,
+                    new DereferenceExpression(
+                        node.origin, VariableRef.wrap(ptrVar), type, simpleLHS.addressSpace),
+                    node.newValueRef(), type),
+                node.resultExp
+            ]));
+            return;
+        }
+        
+        let result = new ReadModifyWriteExpression(node.origin, node.lValue.base, node.lValue.baseType);
+        result.newValueExp = new CommaExpression(node.origin, [
+            node.oldValueVar, node.newValueVar,
+            new Assignment(node.origin, node.oldValueRef(), node.lValue.emitGet(result.oldValueRef()), type),
+            new Assignment(node.origin, node.newValueRef(), node.newValueExp, type),
+            node.lValue.emitSet(result.oldValueRef(), node.newValueRef())
+        ]);
+        result.resultExp = node.newValueRef();
+        this._handleReadModifyWrite(result);
+        node.become(result);
+    }
+    
+    visitReadModifyWriteExpression(node)
+    {
+        node.newValueExp.visit(this);
+        node.resultExp.visit(this);
+        this._visitRValuesWithinLValue(node.lValue);
+        this._handleReadModifyWrite(node);
+    }
+    
+    visitAssignment(node)
+    {
+        this._visitRValuesWithinLValue(node.lhs);
+        node.rhs.visit(this);
+        
+        let simpleLHS = node.lhs.visit(new NormalUsePropertyResolver());
+        if (simpleLHS.isLValue) {
+            node.lhs.become(simpleLHS);
+            return;
+        }
+        
+        if (!(node.lhs instanceof PropertyAccessExpression))
+            throw new Error("Unexpected lhs type: " + node.lhs.constructor.name);
+        
+        let result = new ReadModifyWriteExpression(node.origin, node.lhs.base, node.lhs.baseType);
+        let resultVar = new AnonymousVariable(node.origin, node.type);
+        result.newValueExp = new CommaExpression(node.origin, [
+            resultVar,
+            new Assignment(node.origin, VariableRef.wrap(resultVar), node.rhs, node.type),
+            node.lhs.emitSet(result.oldValueRef(), VariableRef.wrap(resultVar))
+        ]);
+        result.resultExp = VariableRef.wrap(resultVar);
+        this._handleReadModifyWrite(result);
+        node.become(result);
+    }
+    
+    visitMakePtrExpression(node)
+    {
+        super.visitMakePtrExpression(node);
+        if (!node.lValue.isLValue)
+            throw new WTypeError(node.origin.originString, "Not an lvalue: " + node.lValue);
+    }
+    
+    visitMakeArrayRefExpression(node)
+    {
+        super.visitMakeArrayRefExpression(node);
+        if (!node.lValue.isLValue)
+            throw new WTypeError(node.origin.originString, "Not an lvalue: " + node.lValue);
+    }
+}
+
index 86b6183..4502dd8 100644 (file)
@@ -97,16 +97,16 @@ class ProtocolDecl extends Protocol {
     {
         let substitution = new Substitution([this._typeVariable], [type]);
         let signatures = this.signatures;
-        for (let signature of signatures) {
-            signature = signature.visit(substitution);
-            let overload = resolveOverloadImpl(signature.possibleOverloads, signature.typeParameters, signature.parameterTypes, signature.returnTyupeForOverloadResolution);
+        for (let originalSignature of signatures) {
+            let signature = originalSignature.visit(substitution);
+            let overload = resolveOverloadImpl(signature.possibleOverloads, signature.typeParameters, signature.parameterTypes, signature.returnTypeForOverloadResolution);
             if (!overload.func)
-                return {result: false, reason: "Did not find matching signature for " + signature + " with type " + type + (overload.failures.length ? " (tried: " + overload.failures.join("; ") + ")" : "")};
+                return {result: false, reason: "Did not find matching signature for " + originalSignature + " (at " + originalSignature.origin.originString + ") with type " + type + (overload.failures.length ? " (tried: " + overload.failures.join("; ") + ")" : "")};
             
             let substitutedReturnType = overload.func.returnType.substituteToUnification(
                 overload.func.typeParameters, overload.unificationContext);
             if (!substitutedReturnType.equals(signature.returnType))
-                return {result: false, reason: "Return type mismatch between " + signature.returnType + " and " + substitutedReturnType};
+                return {result: false, reason: "At signature " + originalSignature + " (at " + originalSignature.origin.originString + "): return type mismatch between " + signature.returnType + " and " + substitutedReturnType + " in found function " + overload.func.toDeclString()};
         }
         return {result: true};
     }
index 2f8e527..e4cfb8c 100644 (file)
@@ -36,6 +36,19 @@ class PtrType extends ReferenceType {
         return this.elementType.unify(unificationContext, other.elementType);
     }
     
+    argumentForAndOverload(origin, value)
+    {
+        throw new WTypeError(origin.originString, "Pointer subscript is not valid");
+    }
+    argumentTypeForAndOverload(origin, type)
+    {
+        throw new WTypeError(origin.originString, "Pointer subscript is not valid");
+    }
+    returnTypeFromAndOverload(origin)
+    {
+        return this.elementType;
+    }
+
     toString()
     {
         return this.elementType + "^ " + this.addressSpace;
diff --git a/Tools/WebGPUShadingLanguageRI/ReadModifyWriteExpression.js b/Tools/WebGPUShadingLanguageRI/ReadModifyWriteExpression.js
new file mode 100644 (file)
index 0000000..71b62b7
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 ReadModifyWriteExpression extends Expression {
+    constructor(origin, lValue, type = null)
+    {
+        super(origin);
+        this._lValue = lValue;
+        this.oldValueVar = new AnonymousVariable(origin, type);
+        this.newValueVar = new AnonymousVariable(origin, type);
+        this.newValueExp = null;
+        this.resultExp = null;
+    }
+    
+    get lValue() { return this._lValue; }
+    
+    oldValueRef() { return VariableRef.wrap(this.oldValueVar); }
+    newValueRef() { return VariableRef.wrap(this.newValueVar); }
+    
+    toString()
+    {
+        return "RMW(" + this.lValue + ", " + this.oldValueVar + ", " + this.newValueVar + ", " + this.newValueExp + ", " + this.resultExp + ")";
+    }
+}
+
index 5dd814b..1922e8e 100644 (file)
  */
 "use strict";
 
-function resolveNames(program)
+function createNameResolver(program)
 {
-    program.visit(new NameResolver());
+    return new NameResolver(program.globalNameContext);
 }
+
+function resolveNamesInTypes(program, nameResolver)
+{
+    for (let type of program.types.values())
+        nameResolver.doStatement(type);
+}
+
+function resolveNamesInProtocols(program, nameResolver)
+{
+    for (let protocol of program.protocols.values())
+        nameResolver.doStatement(protocol);
+}
+
+function resolveNamesInFunctions(program, nameResolver)
+{
+    for (let funcs of program.functions.values()) {
+        for (let func of funcs)
+            nameResolver.doStatement(func);
+    }
+}
+
index 8fca9ff..d9a7c10 100644 (file)
@@ -26,6 +26,9 @@
 
 function resolveOverloadImpl(functions, typeArguments, argumentTypes, returnType)
 {
+    if (!functions)
+        throw new Error("Null functions; that should have been caught by the caller.");
+    
     let failures = [];
     let successes = [];
     for (let func of functions) {
diff --git a/Tools/WebGPUShadingLanguageRI/ResolveProperties.js b/Tools/WebGPUShadingLanguageRI/ResolveProperties.js
new file mode 100644 (file)
index 0000000..7c6a8e6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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";
+
+// This turns DotExpression and IndexExpression into calls to getters (operator.field and operator[]),
+// setters (operator.field= and operator[]=), and anders (operator&.field and operator&[]). This involves
+// transforming assignments and RMW's to do lvalue emulation.
+function resolveProperties(program)
+{
+    program.visit(new PropertyResolver());
+}
index 70a2d2d..3bd2a4d 100644 (file)
  */
 "use strict";
 
-function resolveTypeDefs(program)
+function resolveTypeDefsInTypes(program)
 {
-    program.visit(new TypeDefResolver());
+    let resolver = new TypeDefResolver();
+    for (let type of program.types.values())
+        type.visit(resolver);
+}
+
+function resolveTypeDefsInProtocols(program)
+{
+    let resolver = new TypeDefResolver();
+    for (let protocol of program.protocols.values())
+        protocol.visit(resolver);
+}
+
+function resolveTypeDefsInFunctions(program)
+{
+    let resolver = new TypeDefResolver();
+    for (let funcs of program.functions.values()) {
+        for (let func of funcs)
+            func.visit(resolver);
+    }
 }
 
index 1da4dc1..b58f4b5 100644 (file)
@@ -76,7 +76,7 @@ class ReturnChecker extends Visitor {
 
     _isBoolCastFromLiteralTrue(node)
     {
-        return node.isCast && node.returnType instanceof TypeRef && node.returnType.equals(this._program.intrinsics.bool) && node.argumentList.length == 1 && node.argumentList[0].list.length == 1 && node.argumentList[0].list[0] instanceof BoolLiteral && node.argumentList[0].list[0].value;
+        return node.isCast && node.returnType instanceof TypeRef && node.returnType.equals(this._program.intrinsics.bool) && node.argumentList.length == 1 && node.argumentList[0] instanceof BoolLiteral && node.argumentList[0].value;
     }
 
     visitWhileLoop(node)
index 1f9b575..6a36f84 100644 (file)
 // FIXME: This should have sensible behavior when it encounters definitions that it cannot handle. Right
 // now we are hackishly preventing this by wrapping things in TypeRef. That's probably wrong.
 // https://bugs.webkit.org/show_bug.cgi?id=176208
-class Rewriter extends VisitorBase {
+class Rewriter {
     constructor()
     {
-        super();
         this._mapping = new Map();
     }
     
@@ -73,7 +72,8 @@ class Rewriter extends VisitorBase {
             node.origin, node.name,
             node.returnType.visit(this),
             node.typeParameters.map(parameter => parameter.visit(this)),
-            node.parameters.map(parameter => parameter.visit(this)));
+            node.parameters.map(parameter => parameter.visit(this)),
+            node.isCast);
         result.protocolDecl = node.protocolDecl;
         result.possibleOverloads = node.possibleOverloads;
         return result;
@@ -99,7 +99,7 @@ class Rewriter extends VisitorBase {
         let result = new VariableDecl(
             node.origin, node.name,
             node.type.visit(this),
-            node.initializer ? node.initializer.visit(this) : null);
+            Node.visit(node.initializer, this));
         this._mapNode(node, result);
         result.ePtr = node.ePtr;
         return result;
@@ -131,7 +131,7 @@ class Rewriter extends VisitorBase {
     visitTypeRef(node)
     {
         let result = new TypeRef(node.origin, node.name, node.typeArguments.map(typeArgument => typeArgument.visit(this)));
-        result.type = node.type ? node.type.visit(this) : null;
+        result.type = Node.visit(node.type, this);
         return result;
     }
     
@@ -163,24 +163,54 @@ class Rewriter extends VisitorBase {
     visitAssignment(node)
     {
         let result = new Assignment(node.origin, node.lhs.visit(this), node.rhs.visit(this));
-        result.type = node.type ? node.type.visit(this) : null;
+        result.type = Node.visit(node.type, this);
+        return result;
+    }
+    
+    visitReadModifyWriteExpression(node)
+    {
+        let result = new ReadModifyWriteExpression(node.origin, node.lValue.visit(this));
+        result.oldValueVar = node.oldValueVar.visit(this);
+        result.newValueVar = node.newValueVar.visit(this);
+        result.newValueExp = node.newValueExp.visit(this);
+        result.resultExp = node.resultExp.visit(this);
         return result;
     }
     
     visitDereferenceExpression(node)
     {
         let result = new DereferenceExpression(node.origin, node.ptr.visit(this));
-        result.type = node.type ? node.type.visit(this) : null;
+        result.type = Node.visit(node.type, this);
+        result.addressSpace = node.addressSpace;
         return result;
     }
     
+    _handlePropertyAccessExpression(result, node)
+    {
+        result.possibleGetOverloads = node.possibleGetOverloads;
+        result.possibleSetOverloads = node.possibleSetOverloads;
+        result.possibleAndOverloads = node.possibleAndOverloads;
+        result.baseType = Node.visit(node.baseType, this);
+        result.callForGet = Node.visit(node.callForGet, this);
+        result.resultTypeForGet = Node.visit(node.resultTypeForGet, this);
+        result.callForAnd = Node.visit(node.callForAnd, this);
+        result.resultTypeForAnd = Node.visit(node.resultTypeForAnd, this);
+        result.callForSet = Node.visit(node.callForSet, this);
+        result.errorForSet = node.errorForSet;
+        result.updateCalls();
+    }
+    
     visitDotExpression(node)
     {
         let result = new DotExpression(node.origin, node.struct.visit(this), node.fieldName);
-        result.structType = node.structType ? node.structType.visit(this) : null;
-        // We don't want to rewrite field, since this is a deep reference going out of the function.
-        // Also, this is set up during inlining, so we're already fully monomorphised.
-        result.field = node.field;
+        this._handlePropertyAccessExpression(result, node);
+        return result;
+    }
+    
+    visitIndexExpression(node)
+    {
+        let result = new IndexExpression(node.origin, node.array.visit(this), node.index.visit(this));
+        this._handlePropertyAccessExpression(result, node);
         return result;
     }
     
@@ -216,7 +246,7 @@ class Rewriter extends VisitorBase {
     
     visitReturn(node)
     {
-        return new Return(node.origin, node.value ? node.value.visit(this) : null);
+        return new Return(node.origin, Node.visit(node.value, this));
     }
     
     visitContinue(node)
@@ -241,7 +271,7 @@ class Rewriter extends VisitorBase {
     visitGenericLiteralType(node)
     {
         let result = new node.constructor(node.origin, node.value);
-        result.type = node.type ? node.type.visit(this) : null;
+        result.type = Node.visit(node.type, this);
         result.preferredType = node.preferredType.visit(this);
         return result;
     }
@@ -262,7 +292,7 @@ class Rewriter extends VisitorBase {
     visitNullType(node)
     {
         let result = new NullType(node.origin);
-        result.type = node.type ? node.type.visit(this) : null;
+        result.type = Node.visit(node.type, this);
         return result;
     }
 
@@ -281,7 +311,7 @@ class Rewriter extends VisitorBase {
         result.possibleOverloads = node.possibleOverloads;
         if (node.isCast)
             result.setCastData(node.returnType.visit(this));
-        result.resultType = node.resultType ? node.resultType.visit(this) : null;
+        result.resultType = Node.visit(node.resultType, this);
         result.resultEPtr = node.resultEPtr;
         return result;
     }
@@ -291,7 +321,7 @@ class Rewriter extends VisitorBase {
         let result = new CallExpression(
             node.origin, node.name,
             node.typeArguments.map(typeArgument => typeArgument.visit(this)),
-            node.argumentList.map(argument => argument.visit(this)));
+            node.argumentList.map(argument => Node.visit(argument, this)));
         return this.processDerivedCallData(node, result);
     }
     
@@ -299,7 +329,7 @@ class Rewriter extends VisitorBase {
     {
         let result = new FunctionLikeBlock(
             node.origin,
-            node.returnType ? node.returnType.visit(this) : null,
+            Node.visit(node.returnType, this),
             node.argumentList.map(argument => argument.visit(this)),
             node.parameters.map(parameter => parameter.visit(this)),
             node.body.visit(this));
@@ -323,7 +353,7 @@ class Rewriter extends VisitorBase {
 
     visitIfStatement(node)
     {
-        return new IfStatement(node.origin, node.conditional.visit(this), node.body.visit(this), node.elseBody ? node.elseBody.visit(this) : undefined);
+        return new IfStatement(node.origin, node.conditional.visit(this), node.body.visit(this), Node.visit(node.elseBody, this));
     }
 
     visitWhileLoop(node)
@@ -339,22 +369,24 @@ class Rewriter extends VisitorBase {
     visitForLoop(node)
     {
         return new ForLoop(node.origin,
-            node.initialization ? node.initialization.visit(this) : undefined,
-            node.condition ? node.condition.visit(this) : undefined,
-            node.increment ? node.increment.visit(this) : undefined,
+            Node.visit(node.initialization, this),
+            Node.visit(node.condition, this),
+            Node.visit(node.increment, this),
             node.body.visit(this));
     }
     
-    visitLetExpression(node)
+    visitAnonymousVariable(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);
+        let result = new AnonymousVariable(node.origin, node.type.visit(this));
+        result._index = node._index;
         this._mapNode(node, result);
-        result.body = node.body.visit(this);
         result.ePtr = node.ePtr;
         return result;
     }
+    
+    visitIdentityExpression(node)
+    {
+        return new IdentityExpression(node.target.visit(this));
+    }
 }
 
index 03e94cc..8f66052 100644 (file)
@@ -65,6 +65,18 @@ native float operator*(float, float);
 native double operator*(double, double);
 native int operator/(int, int);
 native uint operator/(uint, uint);
+native int operator&(int, int);
+native int operator|(int, int);
+native int operator^(int, int);
+native int operator~(int);
+native int operator<<(int, uint);
+native int operator>>(int, uint);
+native uint operator&(uint, uint);
+native uint operator|(uint, uint);
+native uint operator^(uint, uint);
+native uint operator~(uint);
+native uint operator<<(uint, uint);
+native uint operator>>(uint, uint);
 native float operator/(float, float);
 native double operator/(double, double);
 native bool operator==(int, int);
@@ -89,6 +101,35 @@ native bool operator>=(uint, uint);
 native bool operator>=(float, float);
 native bool operator>=(double, double);
 
+bool operator&(bool a, bool b)
+{
+    if (a)
+        return b;
+    return false;
+}
+bool operator|(bool a, bool b)
+{
+    if (a)
+        return true;
+    if (b)
+        return true;
+    return false;
+}
+bool operator^(bool a, bool b)
+{
+    if (a)
+        return !b;
+    return b;
+}
+bool operator~(bool value)
+{
+    return !value;
+}
+
+protocol Addable {
+    Addable operator+(Addable, Addable);
+}
+
 protocol Equatable {
     bool operator==(Equatable, Equatable);
 }
@@ -113,4 +154,14 @@ native thread T^ operator&[]<T>(thread T[], uint);
 native threadgroup T^ operator&[]<T:primitive>(threadgroup T[], uint);
 native device T^ operator&[]<T:primitive>(device T[], uint);
 native constant T^ operator&[]<T:primitive>(constant T[], uint);
+
+native uint operator.length<T>(thread T[]);
+native uint operator.length<T:primitive>(threadgroup T[]);
+native uint operator.length<T:primitive>(device T[]);
+native uint operator.length<T:primitive>(constant T[]);
+
+uint operator.length<T, uint length>(T[length])
+{
+    return length;
+}
 `;
index 3b95cf2..0801ace 100644 (file)
@@ -59,5 +59,11 @@ class StructLayoutBuilder extends Visitor {
             throw new Error("Type does not have size: " + node.type);
         this._offset += size;
     }
+    
+    visitNativeFuncInstance(node)
+    {
+        super.visitNativeFuncInstance(node);
+        node.func.didLayoutStructsInImplementationData(node.implementationData);
+    }
 }
 
index d2c75e1..3ab7514 100644 (file)
@@ -57,21 +57,29 @@ class StructType extends Type {
         return result;
     }
     
-    instantiate(typeArguments, mode)
+    instantiate(typeArguments = null)
     {
-        if (typeArguments.length != this.typeParameters.length)
-            throw new WTypeError(origin.originString, "Wrong number of type arguments to instantiation");
+        let substitution = null;
+        let typeParameters = this.typeParameters;
         
-        if (!typeArguments.length)
-            return this;
+        if (typeArguments) {
+            if (typeArguments.length != this.typeParameters.length)
+                throw new WTypeError(origin.originString, "Wrong number of type arguments to instantiation");
+            
+            if (!typeArguments.length)
+                return this;
+            
+            substitution = new Substitution(this.typeParameters, typeArguments);
+            typeParameters = [];
+        }
         
-        let substitution = new Substitution(this.typeParameters, typeArguments);
-        let instantiateImmediates = mode == "shallow" ? null : new InstantiateImmediates();
-        let result = new StructType(this.origin, this.name, []);
+        let instantiateImmediates = new InstantiateImmediates();
+        let result = new StructType(this.origin, this.name, typeParameters);
         for (let field of this.fields) {
-            let newField = field.visit(substitution);
-            if (instantiateImmediates)
-                newField = newField.visit(instantiateImmediates);
+            let newField = field;
+            if (substitution)
+                newField = newField.visit(substitution);
+            newField = newField.visit(instantiateImmediates);
             result.add(newField);
         }
         return result;
index 60b6d51..b0676ec 100644 (file)
@@ -35,6 +35,8 @@ class Substitution extends Rewriter {
             this._map.set(parameters[i], argumentList[i]);
     }
     
+    get map() { return this._map; }
+    
     visitTypeRef(node)
     {
         let replacement = this._map.get(node.type);
diff --git a/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js b/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js
new file mode 100644 (file)
index 0000000..69bf0ce
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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 synthesizeStructAccessors(program)
+{
+    for (let type of program.types.values()) {
+        if (!(type instanceof StructType))
+            continue;
+        
+        for (let field of type.fields) {
+            function createTypeParameters()
+            {
+                return type.typeParameters.map(
+                    typeParameter => typeParameter.visit(new TypeParameterRewriter()));
+            }
+            
+            function createTypeArguments()
+            {
+                return typeParameters.map(typeParameter => typeParameter.visit(new AutoWrapper()));
+            }
+            
+            function setupImplementationData(nativeFunc, implementation)
+            {
+                nativeFunc.instantiateImplementation = substitution => {
+                    let newType = type.instantiate(nativeFunc.typeParameters.map(typeParameter => {
+                        let substitute = substitution.map.get(typeParameter);
+                        if (!substitute)
+                            throw new Error("Null substitute for type parameter " + typeParameter);
+                        return substitute;
+                    }));
+                    return {type: newType, fieldName: field.name};
+                };
+                nativeFunc.visitImplementationData = (implementationData, visitor) => {
+                    // Visiting the type first ensures that the struct layout builder figures out the field's
+                    // offset.
+                    implementationData.type.visit(visitor);
+                };
+                nativeFunc.didLayoutStructsInImplementationData = implementationData => {
+                    let structSize = implementationData.type.size;
+                    if (structSize == null)
+                        throw new Error("No struct size for " + nativeFunc);
+                    let field = implementationData.type.fieldByName(implementationData.fieldName);
+                    if (!field)
+                        throw new Error("Could not find field");
+                    let offset = field.offset;
+                    let fieldSize = field.type.size;
+                    if (fieldSize == null)
+                        throw new Error("No field size for " + nativeFunc);
+                    if (offset == null)
+                        throw new Error("No offset for " + nativeFunc);
+                    
+                    implementationData.offset = offset;
+                    implementationData.structSize = structSize;
+                    implementationData.fieldSize = fieldSize;
+                };
+                nativeFunc.implementation = (argumentList, node) => {
+                    let nativeFuncInstance = node.nativeFuncInstance;
+                    let implementationData = nativeFuncInstance.implementationData;
+                    return implementation(
+                        argumentList,
+                        implementationData.offset,
+                        implementationData.structSize,
+                        implementationData.fieldSize);
+                };
+            }
+            
+            function createFieldType()
+            {
+                return field.type.visit(new Substitution(type.typeParameters, typeParameters));
+            }
+            
+            function createTypeRef()
+            {
+                return TypeRef.instantiate(type, createTypeArguments());
+            }
+            
+            let isCast = false;
+            let typeParameters;
+            let nativeFunc;
+
+            // The getter: operator.field
+            typeParameters = createTypeParameters();
+            nativeFunc = new NativeFunc(
+                field.origin, "operator." + field.name, createFieldType(), typeParameters,
+                [new FuncParameter(field.origin, null, createTypeRef())], isCast);
+            setupImplementationData(nativeFunc, ([base], offset, structSize, fieldSize) => {
+                let result = new EPtr(new EBuffer(fieldSize), 0);
+                result.copyFrom(base.plus(offset), fieldSize);
+                return result;
+            });
+            program.add(nativeFunc);
+            
+            // The setter: operator.field=
+            typeParameters = createTypeParameters();
+            nativeFunc = new NativeFunc(
+                field.origin, "operator." + field.name + "=", createTypeRef(), typeParameters,
+                [
+                    new FuncParameter(field.origin, null, createTypeRef()),
+                    new FuncParameter(field.origin, null, createFieldType())
+                ],
+                isCast);
+            setupImplementationData(nativeFunc, ([base, value], offset, structSize, fieldSize) => {
+                let result = new EPtr(new EBuffer(structSize), 0);
+                result.copyFrom(base, structSize);
+                result.plus(offset).copyFrom(value, fieldSize);
+                return result;
+            });
+            program.add(nativeFunc);
+            
+            // The ander: operator&.field
+            function setupAnder(addressSpace)
+            {
+                typeParameters = createTypeParameters();
+                nativeFunc = new NativeFunc(
+                    field.origin, "operator&." + field.name, new PtrType(field.origin, addressSpace, createFieldType()),
+                    typeParameters,
+                    [
+                        new FuncParameter(
+                            field.origin, null,
+                            new PtrType(field.origin, addressSpace, createTypeRef()))
+                    ],
+                    isCast);
+                setupImplementationData(nativeFunc, ([base], offset, structSize, fieldSize) => {
+                    base = base.loadValue();
+                    if (!base)
+                        throw new WTrapError(node.origin.originString, "Null dereference");
+                    return EPtr.box(base.plus(offset));
+                });
+                program.add(nativeFunc);
+            }
+            
+            setupAnder("thread");
+            if (type.instantiate().isPrimitive) {
+                setupAnder("threadgroup");
+                setupAnder("device");
+                setupAnder("constant");
+            }
+        }
+    }
+}
index c12fb82..84bae5f 100644 (file)
@@ -6,13 +6,14 @@
 <script src="ReferenceType.js"></script>
 <script src="Value.js"></script>
 <script src="Expression.js"></script>
-<script src="VisitorBase.js"></script>
 <script src="Rewriter.js"></script>
 <script src="Visitor.js"></script>
 <script src="CreateLiteral.js"></script>
 <script src="CreateLiteralType.js"></script>
+<script src="PropertyAccessExpression.js"></script>
 
 <script src="AddressSpace.js"></script>
+<script src="AnonymousVariable.js"></script>
 <script src="ArrayRefType.js"></script>
 <script src="ArrayType.js"></script>
 <script src="Assignment.js"></script>
@@ -20,7 +21,6 @@
 <script src="Block.js"></script>
 <script src="BoolLiteral.js"></script>
 <script src="Break.js"></script>
-<script src="CallAssignment.js"></script>
 <script src="CallExpression.js"></script>
 <script src="CallFunction.js"></script>
 <script src="Check.js"></script>
@@ -48,6 +48,7 @@
 <script src="Evaluator.js"></script>
 <script src="ExpressionFinder.js"></script>
 <script src="Field.js"></script>
+<script src="FindHighZombies.js"></script>
 <script src="FlattenProtocolExtends.js"></script>
 <script src="FloatLiteral.js"></script>
 <script src="FloatLiteralType.js"></script>
 <script src="FuncInstantiator.js"></script>
 <script src="FuncParameter.js"></script>
 <script src="FunctionLikeBlock.js"></script>
+<script src="HighZombieFinder.js"></script>
+<script src="IdentityExpression.js"></script>
 <script src="IfStatement.js"></script>
+<script src="IndexExpression.js"></script>
 <script src="InferTypesForCall.js"></script>
 <script src="Inline.js"></script>
 <script src="Inliner.js"></script>
 <script src="NativeFuncInstance.js"></script>
 <script src="NativeType.js"></script>
 <script src="NativeTypeInstance.js"></script>
+<script src="NormalUsePropertyResolver.js"></script>
 <script src="NullLiteral.js"></script>
 <script src="NullType.js"></script>
 <script src="OverloadResolutionFailure.js"></script>
 <script src="Parse.js"></script>
 <script src="Prepare.js"></script>
+<script src="PropertyResolver.js"></script>
 <script src="Program.js"></script>
 <script src="Protocol.js"></script>
 <script src="ProtocolDecl.js"></script>
 <script src="ProtocolFuncDecl.js"></script>
 <script src="ProtocolRef.js"></script>
 <script src="PtrType.js"></script>
+<script src="ReadModifyWriteExpression.js"></script>
 <script src="RecursionChecker.js"></script>
 <script src="ResolveNames.js"></script>
 <script src="ResolveOverloadImpl.js"></script>
+<script src="ResolveProperties.js"></script>
 <script src="ResolveTypeDefs.js"></script>
 <script src="Return.js"></script>
 <script src="ReturnChecker.js"></script>
 <script src="StructLayoutBuilder.js"></script>
 <script src="StructType.js"></script>
 <script src="Substitution.js"></script>
-<script src="SuffixCallAssignment.js"></script>
+<script src="SynthesizeStructAccessors.js"></script>
 <script src="TypeDef.js"></script>
 <script src="TypeDefResolver.js"></script>
 <script src="TypeOrVariableRef.js"></script>
+<script src="TypeParameterRewriter.js"></script>
 <script src="TypeRef.js"></script>
 <script src="TypeVariable.js"></script>
+<script src="TypeVariableTracker.js"></script>
 <script src="TypedValue.js"></script>
 <script src="UintLiteral.js"></script>
 <script src="UintLiteralType.js"></script>
 <script>
 function doTestInBrowser()
 {
-    try {
-        doTest(window);
-    } catch (e) {
-        print("ERROR: " + e.message);
-        print(e.stack);
+    var tester = doTest(window);
+    var lastTime;
+    function next()
+    {
+        var before = Date.now();
+        for (;;) {
+            try {
+                if (tester.next().done)
+                    return;
+            } catch (e) {
+                print("ERROR: " + e.message);
+                print(e.stack);
+                return;
+            }
+            var now = Date.now();
+            if (now - before > 100) {
+                window.setTimeout(next, 0);
+                return;
+            }
+        }
     }
+    next();
 }
 </script>
 
index 38bda4b..1173295 100644 (file)
@@ -29,6 +29,7 @@ if (this.window) {
         var span = document.createElement("span");
         document.getElementById("messages").appendChild(span);
         span.innerHTML = text.replace(/ /g, "&nbsp;").replace(/\n/g, "<br>") + "<br>";
+        window.scrollTo(0,document.body.scrollHeight);
     };
     this.preciseTime = () => performance.now() / 1000;
 } else
@@ -57,6 +58,14 @@ function makeInt(program, value)
     return TypedValue.box(program.intrinsics.int32, value);
 }
 
+function checkNumber(program, result, expected)
+{
+    if (!result.type.isNumber)
+        throw new Error("Wrong result type; result: " + result);
+    if (result.value != expected)
+        throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
+}
+
 function makeUint(program, value)
 {
     return TypedValue.box(program.intrinsics.uint32, value);
@@ -448,10 +457,10 @@ function TEST_deviceArrayStoreIntLiteral()
 function TEST_simpleProtocol()
 {
     let program = doPrep(`
-        protocol Addable {
-            Addable operator+(Addable, Addable);
+        protocol MyAddable {
+            MyAddable operator+(MyAddable, MyAddable);
         }
-        T add<T:Addable>(T a, T b)
+        T add<T:MyAddable>(T a, T b)
         {
             return a + b;
         }
@@ -781,7 +790,7 @@ function TEST_storeNullArrayRef()
         () => doPrep(`
             void foo() { null[0u] = 42; }
         `),
-        (e) => e instanceof WTypeError && e.message.indexOf("Did not find function for call") != -1);
+        (e) => e instanceof WTypeError && e.message.indexOf("LHS of assignment is not an LValue") != -1);
 }
 
 function TEST_returnNullArrayRef()
@@ -889,14 +898,14 @@ function TEST_badIntLiteralForInt()
 {
     checkFail(
         () => doPrep("void foo() { int x = 3000000000; }"),
-        (e) => e instanceof WTypeError);
+        (e) => e instanceof WSyntaxError);
 }
 
 function TEST_badIntLiteralForUint()
 {
     checkFail(
         () => doPrep("void foo() { uint x = 5000000000; }"),
-        (e) => e instanceof WTypeError);
+        (e) => e instanceof WSyntaxError);
 }
 
 function TEST_badIntLiteralForDouble()
@@ -1505,10 +1514,10 @@ function TEST_intLiteralGeneric()
 function TEST_intLiteralGenericWithProtocols()
 {
     let program = doPrep(`
-        protocol ConvertibleToInt {
-            operator int(ConvertibleToInt);
+        protocol MyConvertibleToInt {
+            operator int(MyConvertibleToInt);
         }
-        int foo<T:ConvertibleToInt>(T x) { return int(x); }
+        int foo<T:MyConvertibleToInt>(T x) { return int(x); }
         int bar() { return foo(42); }
     `);
     checkInt(program, callFunction(program, "bar", [], []), 42);
@@ -1526,10 +1535,10 @@ function TEST_uintLiteralGeneric()
 function TEST_uintLiteralGenericWithProtocols()
 {
     let program = doPrep(`
-        protocol ConvertibleToUint {
-            operator uint(ConvertibleToUint);
+        protocol MyConvertibleToUint {
+            operator uint(MyConvertibleToUint);
         }
-        uint foo<T:ConvertibleToUint>(T x) { return uint(x); }
+        uint foo<T:MyConvertibleToUint>(T x) { return uint(x); }
         uint bar() { return foo(42u); }
     `);
     checkUint(program, callFunction(program, "bar", [], []), 42);
@@ -2604,6 +2613,771 @@ function TEST_makeArrayRefFromArrayRef()
         (e) => e instanceof WTypeError);
 }
 
+function TEST_simpleLength()
+{
+    let program = doPrep(`
+        uint foo()
+        {
+            double[754] array;
+            return (@array).length;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], []), 754);
+}
+
+function TEST_nonArrayRefArrayLength()
+{
+    let program = doPrep(`
+        uint foo()
+        {
+            double[754] array;
+            return array.length;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], []), 754);
+}
+
+function TEST_assignLength()
+{
+    checkFail(
+        () => doPrep(`
+            void foo()
+            {
+                double[754] array;
+                (@array).length = 42;
+            }
+        `),
+        (e) => e instanceof WTypeError && e.message.indexOf("LHS of assignment is not an LValue") != -1);
+}
+
+function TEST_assignLengthHelper()
+{
+    checkFail(
+        () => doPrep(`
+            void bar(thread double[] array)
+            {
+                array.length = 42;
+            }
+            void foo()
+            {
+                double[754] array;
+                bar(@array);
+            }
+        `),
+        (e) => e instanceof WTypeError && e.message.indexOf("Cannot emit set because: Did not find any functions named operator.length=") != -1);
+}
+
+function TEST_simpleGetter()
+{
+    let program = doPrep(`
+        struct Foo {
+            int x;
+        }
+        int operator.y(Foo foo)
+        {
+            return foo.x;
+        }
+        int foo()
+        {
+            Foo foo;
+            foo.x = 7804;
+            return foo.y;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], []), 7804);
+}
+
+function TEST_simpleSetter()
+{
+    let program = doPrep(`
+        struct Foo {
+            int x;
+        }
+        int operator.y(Foo foo)
+        {
+            return foo.x;
+        }
+        Foo operator.y=(Foo foo, int value)
+        {
+            foo.x = value;
+            return foo;
+        }
+        int foo()
+        {
+            Foo foo;
+            foo.y = 7804;
+            return foo.x;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], []), 7804);
+}
+
+function TEST_genericAccessors()
+{
+    let program = doPrep(`
+        struct Foo<T> {
+            T x;
+            T[3] y;
+        }
+        struct Bar<T> {
+            T x;
+            T y;
+        }
+        Bar<T> operator.z<T>(Foo<T> foo)
+        {
+            Bar<T> result;
+            result.x = foo.x;
+            result.y = foo.y[1];
+            return result;
+        }
+        Foo<T> operator.z=<T>(Foo<T> foo, Bar<T> bar)
+        {
+            foo.x = bar.x;
+            foo.y[1] = bar.y;
+            return foo;
+        }
+        T operator.sum<T:Addable>(Foo<T> foo)
+        {
+            return foo.x + foo.y[0] + foo.y[1] + foo.y[2];
+        }
+        T operator.sum<T:Addable>(Bar<T> bar)
+        {
+            return bar.x + bar.y;
+        }
+        operator<T> Bar<T>(T x, T y)
+        {
+            Bar<T> result;
+            result.x = x;
+            result.y = y;
+            return result;
+        }
+        void setup(thread Foo<int>^ foo)
+        {
+            foo->x = 1;
+            foo->y[0] = 2;
+            foo->y[1] = 3;
+            foo->y[2] = 4;
+        }
+        int testSuperBasic()
+        {
+            Foo<int> foo;
+            setup(&foo);
+            return foo.sum;
+        }
+        int testZSetterDidSetY()
+        {
+            Foo<int> foo;
+            foo.z = Bar<int>(53, 932);
+            return foo.y[1];
+        }
+        int testZSetter()
+        {
+            Foo<int> foo;
+            foo.z = Bar<int>(53, 932);
+            return foo.sum;
+        }
+        int testZGetter()
+        {
+            Foo<int> foo;
+            // This deliberately does not call setup() just so we test this syntax.
+            foo.x = 1;
+            foo.y[0] = 2;
+            foo.y[1] = 3;
+            foo.y[2] = 4;
+            return foo.z.sum;
+        }
+        int testLValueEmulation()
+        {
+            Foo<int> foo;
+            setup(&foo);
+            foo.z.y *= 5;
+            return foo.sum;
+        }
+    `);
+    checkInt(program, callFunction(program, "testSuperBasic", [], []), 1 + 2 + 3 + 4);
+    checkInt(program, callFunction(program, "testZSetterDidSetY", [], []), 932);
+    checkInt(program, callFunction(program, "testZSetter", [], []), 53 + 932);
+    checkInt(program, callFunction(program, "testZGetter", [], []), 1 + 3);
+    checkInt(program, callFunction(program, "testLValueEmulation", [], []), 1 + 2 + 3 * 5 + 4);
+}
+
+function TEST_bitSubscriptAccessor()
+{
+    let program = doPrep(`
+        protocol MyBitmaskable : Equatable {
+            MyBitmaskable operator&(MyBitmaskable, MyBitmaskable);
+            MyBitmaskable operator|(MyBitmaskable, MyBitmaskable);
+            MyBitmaskable operator~(MyBitmaskable);
+            MyBitmaskable operator<<(MyBitmaskable, uint);
+            MyBitmaskable operator>>(MyBitmaskable, uint);
+            operator MyBitmaskable(int);
+        }
+        T maskForBitIndex<T:MyBitmaskable>(uint index)
+        {
+            return T(1) << index;
+        }
+        bool operator[]<T:MyBitmaskable>(T value, uint index)
+        {
+            return bool(value & maskForBitIndex<T>(index));
+        }
+        T operator[]=<T:MyBitmaskable>(T value, uint index, bool bit)
+        {
+            T mask = maskForBitIndex<T>(index);
+            if (bit)
+                value |= mask;
+            else
+                value &= ~mask;
+            return value;
+        }
+        uint operator.length(int)
+        {
+            return 32;
+        }
+        uint operator.length(uint)
+        {
+            return 32;
+        }
+        int testIntSetBit3()
+        {
+            int foo;
+            foo[3] = true;
+            return foo;
+        }
+        bool testIntSetGetBit5()
+        {
+            int foo;
+            foo[5] = true;
+            return foo[5];
+        }
+        bool testIntGetBit1()
+        {
+            int foo;
+            return foo[1];
+        }
+        int testUintSumBits()
+        {
+            int foo = 42;
+            int result;
+            for (uint i = 0; i < foo.length; ++i) {
+                if (foo[i])
+                    result++;
+            }
+            return result;
+        }
+        int testUintSwapBits()
+        {
+            int foo = 42;
+            for (uint i = 0; i < foo.length / 2; ++i) {
+                bool tmp = foo[i];
+                foo[i] = foo[foo.length - i - 1];
+                foo[foo.length - i - 1] = tmp;
+            }
+            return foo;
+        }
+        struct Foo {
+            uint f;
+            uint g;
+        }
+        operator Foo(uint f, uint g)
+        {
+            Foo result;
+            result.f = f;
+            result.g = g;
+            return result;
+        }
+        int operator.h(Foo foo)
+        {
+            return int((foo.f & 0xffff) | ((foo.g & 0xffff) << 16));
+        }
+        Foo operator.h=(Foo foo, int value)
+        {
+            foo.f &= ~0xffffu;
+            foo.f |= uint(value) & 0xffff;
+            foo.g &= ~0xffffu;
+            foo.g |= (uint(value) >> 16) & 0xffff;
+            return foo;
+        }
+        int testLValueEmulation()
+        {
+            Foo foo;
+            foo.f = 42;
+            foo.g = 37;
+            for (uint i = 0; i < foo.h.length; ++i)
+                foo.h[i] ^= true;
+            return int(foo.f + foo.g);
+        }
+        struct Bar {
+            Foo a;
+            Foo b;
+        }
+        Foo operator.c(Bar bar)
+        {
+            return Foo(uint(bar.a.h), uint(bar.b.h));
+        }
+        Bar operator.c=(Bar bar, Foo foo)
+        {
+            bar.a.h = int(foo.f);
+            bar.b.h = int(foo.g);
+            return bar;
+        }
+        int testCrazyLValueEmulation()
+        {
+            Bar bar;
+            bar.a.f = 1;
+            bar.a.g = 2;
+            bar.b.f = 3;
+            bar.b.g = 4;
+            for (uint i = 0; i < bar.c.h.length; i += 2)
+                bar.c.h[i] ^= true;
+            return int(bar.a.f + bar.a.g + bar.b.f + bar.b.g);
+        }
+    `);
+    checkInt(program, callFunction(program, "testIntSetBit3", [], []), 8);
+    checkBool(program, callFunction(program, "testIntSetGetBit5", [], []), true);
+    checkBool(program, callFunction(program, "testIntGetBit1", [], []), false);
+    checkInt(program, callFunction(program, "testUintSumBits", [], []), 3);
+    checkInt(program, callFunction(program, "testUintSwapBits", [], []), 1409286144);
+    checkInt(program, callFunction(program, "testLValueEmulation", [], []), 130991);
+    checkInt(program, callFunction(program, "testCrazyLValueEmulation", [], []), 43696);
+}
+
+function TEST_nestedSubscriptLValueEmulationSimple()
+{
+    let program = doPrep(`
+        struct Foo {
+            int[7] array;
+        }
+        int operator[](Foo foo, uint index)
+        {
+            return foo.array[index];
+        }
+        Foo operator[]=(Foo foo, uint index, int value)
+        {
+            foo.array[index] = value;
+            return foo;
+        }
+        uint operator.length(Foo foo)
+        {
+            return foo.array.length;
+        }
+        int sum(Foo foo)
+        {
+            int result = 0;
+            for (uint i = foo.length; i--;)
+                result += foo[i];
+            return result;
+        }
+        struct Bar {
+            Foo[6] array;
+        }
+        uint operator.length(Bar bar)
+        {
+            return bar.array.length;
+        }
+        Foo operator[](Bar bar, uint index)
+        {
+            return bar.array[index];
+        }
+        Bar operator[]=(Bar bar, uint index, Foo value)
+        {
+            bar.array[index] = value;
+            return bar;
+        }
+        int sum(Bar bar)
+        {
+            int result = 0;
+            for (uint i = bar.length; i--;)
+                result += sum(bar[i]);
+            return result;
+        }
+        struct Baz {
+            Bar[5] array;
+        }
+        Bar operator[](Baz baz, uint index)
+        {
+            return baz.array[index];
+        }
+        Baz operator[]=(Baz baz, uint index, Bar value)
+        {
+            baz.array[index] = value;
+            return baz;
+        }
+        uint operator.length(Baz baz)
+        {
+            return baz.array.length;
+        }
+        int sum(Baz baz)
+        {
+            int result = 0;
+            for (uint i = baz.length; i--;)
+                result += sum(baz[i]);
+            return result;
+        }
+        void setValues(thread Baz^ baz)
+        {
+            for (uint i = baz->length; i--;) {
+                for (uint j = (^baz)[i].length; j--;) {
+                    for (uint k = (^baz)[i][j].length; k--;)
+                        (^baz)[i][j][k] = int(i + j + k);
+                }
+            }
+        }
+        int testSetValuesAndSum()
+        {
+            Baz baz;
+            setValues(&baz);
+            return sum(baz);
+        }
+        int testSetValuesMutateValuesAndSum()
+        {
+            Baz baz;
+            setValues(&baz);
+            for (uint i = baz.length; i--;) {
+                for (uint j = baz[i].length; j--;) {
+                    for (uint k = baz[i][j].length; k--;)
+                        baz[i][j][k] *= int(k);
+                }
+            }
+            return sum(baz);
+        }
+    `);
+    checkInt(program, callFunction(program, "testSetValuesAndSum", [], []), 1575);
+    checkInt(program, callFunction(program, "testSetValuesMutateValuesAndSum", [], []), 5565);
+}
+
+function TEST_nestedSubscriptLValueEmulationGeneric()
+{
+    let program = doPrep(`
+        struct Foo<T> {
+            T[7] array;
+        }
+        T operator[]<T>(Foo<T> foo, uint index)
+        {
+            return foo.array[index];
+        }
+        Foo<T> operator[]=<T>(Foo<T> foo, uint index, T value)
+        {
+            foo.array[index] = value;
+            return foo;
+        }
+        uint operator.length<T>(Foo<T> foo)
+        {
+            return foo.array.length;
+        }
+        protocol MyAddable {
+            MyAddable operator+(MyAddable, MyAddable);
+        }
+        T sum<T:MyAddable>(Foo<T> foo)
+        {
+            T result;
+            for (uint i = foo.length; i--;)
+                result += foo[i];
+            return result;
+        }
+        struct Bar<T> {
+            Foo<T>[6] array;
+        }
+        uint operator.length<T>(Bar<T> bar)
+        {
+            return bar.array.length;
+        }
+        Foo<T> operator[]<T>(Bar<T> bar, uint index)
+        {
+            return bar.array[index];
+        }
+        Bar<T> operator[]=<T>(Bar<T> bar, uint index, Foo<T> value)
+        {
+            bar.array[index] = value;
+            return bar;
+        }
+        T sum<T:MyAddable>(Bar<T> bar)
+        {
+            T result;
+            for (uint i = bar.length; i--;)
+                result += sum(bar[i]);
+            return result;
+        }
+        struct Baz<T> {
+            Bar<T>[5] array;
+        }
+        Bar<T> operator[]<T>(Baz<T> baz, uint index)
+        {
+            return baz.array[index];
+        }
+        Baz<T> operator[]=<T>(Baz<T> baz, uint index, Bar<T> value)
+        {
+            baz.array[index] = value;
+            return baz;
+        }
+        uint operator.length<T>(Baz<T> baz)
+        {
+            return baz.array.length;
+        }
+        T sum<T:MyAddable>(Baz<T> baz)
+        {
+            T result;
+            for (uint i = baz.length; i--;)
+                result += sum(baz[i]);
+            return result;
+        }
+        protocol MyConvertibleFromUint {
+            operator MyConvertibleFromUint(uint);
+        }
+        protocol SetValuable : MyAddable, MyConvertibleFromUint { }
+        void setValues<T:SetValuable>(thread Baz<T>^ baz)
+        {
+            for (uint i = baz->length; i--;) {
+                for (uint j = (^baz)[i].length; j--;) {
+                    for (uint k = (^baz)[i][j].length; k--;)
+                        (^baz)[i][j][k] = T(i + j + k);
+                }
+            }
+        }
+        int testSetValuesAndSum()
+        {
+            Baz<int> baz;
+            setValues(&baz);
+            return sum(baz);
+        }
+        int testSetValuesMutateValuesAndSum()
+        {
+            Baz<int> baz;
+            setValues(&baz);
+            for (uint i = baz.length; i--;) {
+                for (uint j = baz[i].length; j--;) {
+                    for (uint k = baz[i][j].length; k--;)
+                        baz[i][j][k] *= int(k);
+                }
+            }
+            return sum(baz);
+        }
+    `);
+    checkInt(program, callFunction(program, "testSetValuesAndSum", [], []), 1575);
+    checkInt(program, callFunction(program, "testSetValuesMutateValuesAndSum", [], []), 5565);
+}
+
+function TEST_boolBitAnd()
+{
+    let program = doPrep(`
+        bool foo(bool a, bool b)
+        {
+            return a & b;
+        }
+    `);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, false)]), false);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, false)]), false);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, true)]), false);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, true)]), true);
+}
+
+function TEST_boolBitOr()
+{
+    let program = doPrep(`
+        bool foo(bool a, bool b)
+        {
+            return a | b;
+        }
+    `);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, false)]), false);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, false)]), true);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, true)]), true);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, true)]), true);
+}
+
+function TEST_boolBitXor()
+{
+    let program = doPrep(`
+        bool foo(bool a, bool b)
+        {
+            return a ^ b;
+        }
+    `);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, false)]), false);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, false)]), true);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, true)]), true);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, true)]), false);
+}
+
+function TEST_boolBitNot()
+{
+    let program = doPrep(`
+        bool foo(bool a)
+        {
+            return ~a;
+        }
+    `);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, false)]), true);
+    checkBool(program, callFunction(program, "foo", [], [makeBool(program, true)]), false);
+}
+
+function TEST_intBitAnd()
+{
+    let program = doPrep(`
+        int foo(int a, int b)
+        {
+            return a & b;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1), makeInt(program, 7)]), 1);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 65535), makeInt(program, 42)]), 42);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, -1), makeInt(program, -7)]), -7);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0), makeInt(program, 85732)]), 0);
+}
+
+function TEST_intBitOr()
+{
+    let program = doPrep(`
+        int foo(int a, int b)
+        {
+            return a | b;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1), makeInt(program, 7)]), 7);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 65535), makeInt(program, 42)]), 65535);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, -1), makeInt(program, -7)]), -1);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0), makeInt(program, 85732)]), 85732);
+}
+
+function TEST_intBitXor()
+{
+    let program = doPrep(`
+        int foo(int a, int b)
+        {
+            return a ^ b;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1), makeInt(program, 7)]), 6);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 65535), makeInt(program, 42)]), 65493);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, -1), makeInt(program, -7)]), 6);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0), makeInt(program, 85732)]), 85732);
+}
+
+function TEST_intBitNot()
+{
+    let program = doPrep(`
+        int foo(int a)
+        {
+            return ~a;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), -2);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 65535)]), -65536);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, -1)]), 0);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), -1);
+}
+
+function TEST_intLShift()
+{
+    let program = doPrep(`
+        int foo(int a, uint b)
+        {
+            return a << b;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1), makeUint(program, 7)]), 128);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 65535), makeUint(program, 2)]), 262140);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, -1), makeUint(program, 5)]), -32);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0), makeUint(program, 3)]), 0);
+}
+
+function TEST_intRShift()
+{
+    let program = doPrep(`
+        int foo(int a, uint b)
+        {
+            return a >> b;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1), makeUint(program, 7)]), 0);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 65535), makeUint(program, 2)]), 16383);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, -1), makeUint(program, 5)]), -1);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0), makeUint(program, 3)]), 0);
+}
+
+function TEST_uintBitAnd()
+{
+    let program = doPrep(`
+        uint foo(uint a, uint b)
+        {
+            return a & b;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 1), makeUint(program, 7)]), 1);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 65535), makeUint(program, 42)]), 42);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, -1), makeUint(program, -7)]), 4294967289);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0), makeUint(program, 85732)]), 0);
+}
+
+function TEST_uintBitOr()
+{
+    let program = doPrep(`
+        uint foo(uint a, uint b)
+        {
+            return a | b;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 1), makeUint(program, 7)]), 7);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 65535), makeUint(program, 42)]), 65535);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, -1), makeUint(program, -7)]), 4294967295);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0), makeUint(program, 85732)]), 85732);
+}
+
+function TEST_uintBitXor()
+{
+    let program = doPrep(`
+        uint foo(uint a, uint b)
+        {
+            return a ^ b;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 1), makeUint(program, 7)]), 6);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 65535), makeUint(program, 42)]), 65493);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, -1), makeUint(program, -7)]), 6);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0), makeUint(program, 85732)]), 85732);
+}
+
+function TEST_uintBitNot()
+{
+    let program = doPrep(`
+        uint foo(uint a)
+        {
+            return ~a;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 1)]), 4294967294);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 65535)]), 4294901760);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, -1)]), 0);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0)]), 4294967295);
+}
+
+function TEST_uintLShift()
+{
+    let program = doPrep(`
+        uint foo(uint a, uint b)
+        {
+            return a << b;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 1), makeUint(program, 7)]), 128);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 65535), makeUint(program, 2)]), 262140);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, -1), makeUint(program, 5)]), 4294967264);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0), makeUint(program, 3)]), 0);
+}
+
+function TEST_uintRShift()
+{
+    let program = doPrep(`
+        uint foo(uint a, uint b)
+        {
+            return a >> b;
+        }
+    `);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 1), makeUint(program, 7)]), 0);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 65535), makeUint(program, 2)]), 16383);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, -1), makeUint(program, 5)]), 134217727);
+    checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0), makeUint(program, 3)]), 0);
+}
+
 function TEST_floatMath()
 {
     let program = doPrep(`
@@ -2792,6 +3566,29 @@ function TEST_floatMath()
         (e) => e instanceof WTypeError);
 }
 
+function TEST_genericCastInfer()
+{
+    let program = doPrep(`
+        struct Complex<T> {
+            T real;
+            T imag;
+        }
+        operator<T> Complex<T>(T real, T imag)
+        {
+            Complex<T> result;
+            result.real = real;
+            result.imag = imag;
+            return result;
+        }
+        int foo()
+        {
+            Complex<int> x = Complex<int>(1, 2);
+            return x.real + x.imag;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], []), 3);
+}
+
 function TEST_booleanMath()
 {
     let program = doPrep(`
@@ -2851,7 +3648,7 @@ if (this["arguments"]) {
     }
 }
 
-function doTest(object)
+function* doTest(object)
 {
     let before = preciseTime();
 
@@ -2860,13 +3657,17 @@ function doTest(object)
             print(s + "...");
             object[s]();
             print("    OK!");
+            yield;
         }
     }
 
     let after = preciseTime();
     
+    print("Success!");
     print("That took " + (after - before) * 1000 + " ms.");
 }
 
 if (!this.window)
-    doTest(this);
+    for (let _ of doTest(this)) { }
+
+
index 0c11f6f..751d7ae 100644 (file)
@@ -29,6 +29,7 @@ class Type extends Node {
     get kind() { return Type; }
     get isPtr() { return false; }
     get isArrayRef() { return false; }
+    get isRef() { return this.isPtr || this.isArrayRef; }
     get isNumber() { return false; }
     get isInt() { return false; }
     get isFloating() { return false; }
@@ -41,5 +42,19 @@ class Type extends Node {
     }
     
     get instantiatedType() { return this.visit(new InstantiateImmediates()); }
+    
+    // Have to call these on the unifyNode.
+    argumentForAndOverload(origin, value)
+    {
+        return new MakePtrExpression(origin, value);
+    }
+    argumentTypeForAndOverload(origin)
+    {
+        return new PtrType(origin, "thread", this);
+    }
+    returnTypeFromAndOverload(origin)
+    {
+        throw new WTypeError(origin.originString, "By-pointer overload returned non-pointer type: " + this);
+    }
 }
 
diff --git a/Tools/WebGPUShadingLanguageRI/TypeParameterRewriter.js b/Tools/WebGPUShadingLanguageRI/TypeParameterRewriter.js
new file mode 100644 (file)
index 0000000..3bd037c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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 TypeParameterRewriter {
+    visitConstexprTypeParameter(node)
+    {
+        return new ConstexprTypeParameter(node.origin, node.name, node.type.visit(new Rewriter()));
+    }
+    
+    visitTypeVariable(node)
+    {
+        return new TypeVariable(node.origin, node.name, node.protocol);
+    }
+}
+
  */
 "use strict";
 
-class CallAssignment extends Expression {
-    constructor(origin, name, lhs, rhs)
+class TypeVariableTracker extends Visitor {
+    constructor()
     {
-        super(origin);
-        this._name = name;
-        this._lhs = lhs;
-        this._rhs = rhs;
-        this.func = null;
+        super();
+        this._set = new Set();
     }
     
-    get name() { return this._name; }
-    get lhs() { return this._lhs; }
-    get rhs() { return this._rhs; } // This can be null.
+    get set() { return this._set; }
     
-    toString()
+    _consider(thing)
     {
-        return this.lhs + " " + this.name + (this.rhs ? "= " + this.rhs : "");
+        if (thing.isUnifiable)
+            this._set.add(thing);
+    }
+    
+    visitTypeRef(node)
+    {
+        this._consider(node.type);
+    }
+    
+    visitVariableRef(node)
+    {
+        this._consider(this.variable);
     }
 }
 
index 99600a8..89e108d 100644 (file)
@@ -28,5 +28,14 @@ class Value extends Node {
     get kind() { return Value; }
     get isConstexpr() { return false; }
     get isLValue() { return false; }
+    
+    become(otherValue)
+    {
+        // NOTE: Make sure that IdentityExpression implements unifyNode and all that
+        let origin = this.origin;
+        this.__proto__ = IdentityExpression.prototype;
+        this._origin = origin;
+        this._target = otherValue;
+    }
 }
 
index 7bc1c4f..e194dd0 100644 (file)
@@ -41,10 +41,9 @@ class VariableRef extends Expression {
     
     get name() { return this._name; }
     get isConstexpr() { return this.variable.isConstexpr; }
-    get unifyNode() { return this.variable.unifyNode; }
+    get unifyNode() { return this.variable.unifyNode; } // This only makes sense when this is a constexpr.
     get isLValue() { return true; }
     get addressSpace() { return "thread"; }
-    get unifyNode() { return this.variable; }
     
     toString()
     {
index 1ba40e0..4c85637 100644 (file)
  */
 "use strict";
 
-class Visitor extends VisitorBase {
-    constructor()
-    {
-        super();
-    }
-    
+class Visitor {
     visitProgram(node)
     {
         for (let statement of node.topLevelStatements)
@@ -68,8 +63,8 @@ class Visitor extends VisitorBase {
     
     visitNativeFuncInstance(node)
     {
-        node.func.visit(this);
         this.visitFunc(node);
+        node.func.visitImplementationData(node.implementationData, this);
     }
     
     visitBlock(node)
@@ -100,8 +95,7 @@ class Visitor extends VisitorBase {
     {
         for (let typeArgument of node.typeArguments)
             typeArgument.visit(this);
-        if (node.type)
-            node.type.visit(this);
+        Node.visit(node.type, this);
     }
     
     visitNativeType(node)
@@ -134,8 +128,7 @@ class Visitor extends VisitorBase {
     
     visitTypeVariable(node)
     {
-        if (node.protocol)
-            node.protocol.visit(this);
+        Node.visit(node.protocol, this);
     }
     
     visitConstexprTypeParameter(node)
@@ -177,14 +170,23 @@ class Visitor extends VisitorBase {
     visitVariableDecl(node)
     {
         node.type.visit(this);
-        if (node.initializer)
-            node.initializer.visit(this);
+        Node.visit(node.initializer, this);
     }
     
     visitAssignment(node)
     {
         node.lhs.visit(this);
         node.rhs.visit(this);
+        Node.visit(node.type, this);
+    }
+    
+    visitReadModifyWriteExpression(node)
+    {
+        node.lValue.visit(this);
+        node.oldValueVar.visit(this);
+        node.newValueVar.visit(this);
+        node.newValueExp.visit(this);
+        node.resultExp.visit(this);
     }
     
     visitDereferenceExpression(node)
@@ -192,11 +194,27 @@ class Visitor extends VisitorBase {
         node.ptr.visit(this);
     }
     
+    _handlePropertyAccessExpression(node)
+    {
+        Node.visit(node.baseType, this);
+        Node.visit(node.callForGet, this);
+        Node.visit(node.resultTypeForGet, this);
+        Node.visit(node.callForAnd, this);
+        Node.visit(node.resultTypeForAnd, this);
+        Node.visit(node.callForSet, this);
+    }
+    
     visitDotExpression(node)
     {
         node.struct.visit(this);
-        if (node.structType)
-            node.structType.visit(this);
+        this._handlePropertyAccessExpression(node);
+    }
+    
+    visitIndexExpression(node)
+    {
+        node.array.visit(this);
+        node.index.visit(this);
+        this._handlePropertyAccessExpression(node);
     }
     
     visitMakePtrExpression(node)
@@ -207,8 +225,7 @@ class Visitor extends VisitorBase {
     visitMakeArrayRefExpression(node)
     {
         node.lValue.visit(this);
-        if (node.numElements)
-            node.numElements.visit(this);
+        Node.visit(node.numElements, this);
     }
     
     visitConvertPtrToArrayRefExpression(node)
@@ -224,8 +241,7 @@ class Visitor extends VisitorBase {
     {
         node.conditional.visit(this);
         node.body.visit(this);
-        if (node.elseBody)
-            node.elseBody.visit(this);
+        Node.visit(node.elseBody, this);
     }
     
     visitWhileLoop(node)
@@ -242,19 +258,15 @@ class Visitor extends VisitorBase {
     
     visitForLoop(node)
     {
-        if (node.initialization)
-            node.initialization.visit(this);
-        if (node.condition)
-            node.condition.visit(this);
-        if (node.increment)
-            node.increment.visit(this);
+        Node.visit(node.initialization, this);
+        Node.visit(node.condition, this);
+        Node.visit(node.increment, this);
         node.body.visit(this);
     }
 
     visitReturn(node)
     {
-        if (node.value)
-            node.value.visit(this);
+        Node.visit(node.value, this);
     }
 
     visitContinue(node)
@@ -272,8 +284,7 @@ class Visitor extends VisitorBase {
     
     visitGenericLiteralType(node)
     {
-        if (node.type)
-            node.type.visit(this);
+        Node.visit(node.type, this);
         node.preferredType.visit(this);
     }
     
@@ -288,8 +299,7 @@ class Visitor extends VisitorBase {
     
     visitNullType(node)
     {
-        if (node.type)
-            node.type.visit(this);
+        Node.visit(node.type, this);
     }
     
     visitCallExpression(node)
@@ -297,16 +307,15 @@ class Visitor extends VisitorBase {
         for (let typeArgument of node.typeArguments)
             typeArgument.visit(this);
         for (let argument of node.argumentList)
-            argument.visit(this);
+            Node.visit(argument, this);
         let actualTypeArguments = node.actualTypeArguments;
         if (actualTypeArguments) {
             for (let argument of actualTypeArguments)
                 argument.visit(this);
         }
-        if (node.returnType)
-            node.returnType.visit(this);
-        if (node.resultType)
-            node.resultType.visit(this);
+        Node.visit(node.nativeFuncInstance, this);
+        Node.visit(node.returnType, this);
+        Node.visit(node.resultType, this);
     }
     
     visitLogicalNot(node)
@@ -322,21 +331,23 @@ class Visitor extends VisitorBase {
     
     visitFunctionLikeBlock(node)
     {
-        if (node.returnType)
-            node.returnType.visit(this);
+        Node.visit(node.returnType, this);
         for (let argument of node.argumentList)
             argument.visit(this);
         for (let parameter of node.parameters)
             parameter.visit(this);
         node.body.visit(this);
-        if (node.resultType)
-            node.resultType.visit(this);
+        Node.visit(node.resultType, this);
     }
     
-    visitLetExpression(node)
+    visitAnonymousVariable(node)
     {
-        node.argument.visit(this);
-        node.body.visit(this);
+        Node.visit(node.type, this);
+    }
+    
+    visitIdentityExpression(node)
+    {
+        node.target.visit(this);
     }
 }