WSL should allow you to say "protocol Foo : Bar { ... }"
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Sep 2017 22:04:01 +0000 (22:04 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Sep 2017 22:04:01 +0000 (22:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176238

Reviewed by Mylex Maxfield.

This makes protocol "subtyping" work. It's not really subtyping. Protocol A inherits protocol B
if every signature in B can be resolved against some signature in A. Also, you can explicitly
subtype protocols using extends syntax ("protocol Foo : Bar, Baz { ... }").

Also makes the diagnostics when type variable constraints fail a _lot_ better. This will even
tell you diagnostics about why a function didn't match when protocols are not inheriting, and
then it will tell you why the inheritance failed.

* WebGPUShadingLanguageRI/All.js:
* WebGPUShadingLanguageRI/AutoWrapper.js:
(AutoWrapper.prototype.visitTypeVariable):
(AutoWrapper):
* WebGPUShadingLanguageRI/Checker.js:
(Checker.prototype.visitProtocolDecl.set throw):
* WebGPUShadingLanguageRI/ConstexprTypeParameter.js:
(ConstexprTypeParameter.prototype.verifyAsArgument):
(ConstexprTypeParameter.prototype.verifyAsParameter):
* WebGPUShadingLanguageRI/FlattenProtocolExtends.js: Added.
* WebGPUShadingLanguageRI/Func.js:
(Func.prototype.get typeParametersForCallResolution):
* WebGPUShadingLanguageRI/InferTypesForCall.js:
(inferTypesForCall):
* WebGPUShadingLanguageRI/IntLiteralType.js:
(IntLiteralType.prototype.verifyAsArgument):
* WebGPUShadingLanguageRI/NameResolver.js:
(NameResolver.prototype.visitProtocolDecl):
(NameResolver.prototype.visitProtocolRef): Deleted.
(NameResolver.prototype.visitProtocolFuncDecl): Deleted.
(NameResolver.prototype.visitTypeDef): Deleted.
(NameResolver.prototype.visitStructType): Deleted.
(NameResolver.prototype._resolveTypeArguments): Deleted.
(NameResolver.prototype.visitTypeRef): Deleted.
(NameResolver.prototype.visitReferenceType): Deleted.
(NameResolver.prototype.visitVariableDecl): Deleted.
(NameResolver.prototype.visitVariableRef): Deleted.
(NameResolver.prototype.visitReturn): Deleted.
(NameResolver.prototype.visitCallExpression): Deleted.
* WebGPUShadingLanguageRI/Node.js:
(Node.prototype.equals):
* WebGPUShadingLanguageRI/NullType.js:
(NullType.prototype.verifyAsArgument):
* WebGPUShadingLanguageRI/Parse.js:
(parseProtocolDecl):
* WebGPUShadingLanguageRI/Prepare.js:
(prepare):
* WebGPUShadingLanguageRI/ProtocolDecl.js:
(ProtocolDecl): Deleted.
(ProtocolDecl.prototype.add): Deleted.
(ProtocolDecl.prototype.get signatures): Deleted.
(ProtocolDecl.prototype.signaturesByName): Deleted.
(ProtocolDecl.prototype.get typeVariable): Deleted.
(ProtocolDecl.prototype.signaturesByNameWithTypeVariable): Deleted.
(ProtocolDecl.prototype.inherits): Deleted.
(ProtocolDecl.prototype.hasHeir): Deleted.
(ProtocolDecl.prototype.toString): Deleted.
* WebGPUShadingLanguageRI/ProtocolFuncDecl.js:
(ProtocolFuncDecl.prototype.get typeParametersForCallResolution):
(ProtocolFuncDecl):
* WebGPUShadingLanguageRI/Rewriter.js:
(Rewriter.prototype.visitProtocolDecl):
* WebGPUShadingLanguageRI/Test.html:
* WebGPUShadingLanguageRI/Test.js:
(TEST_nullTypeVariableUnify):
(TEST_simpleProtocolExtends):
(TEST_protocolExtendsTwo):
* WebGPUShadingLanguageRI/Type.js:
(Type.prototype.inherits):
* WebGPUShadingLanguageRI/TypeDefResolver.js:
(TypeDefResolver.prototype.visitTypeRef):
(TypeDefResolver):
* WebGPUShadingLanguageRI/TypeVariable.js:
(TypeVariable.prototype.inherits):
(TypeVariable.prototype.verifyAsArgument):
(TypeVariable.prototype.verifyAsParameter):
* WebGPUShadingLanguageRI/UnificationContext.js:
(UnificationContext.prototype.verify):
* WebGPUShadingLanguageRI/Visitor.js:
(Visitor.prototype.visitProtocolDecl):
(Visitor.prototype.visitTypeRef): Deleted.
(Visitor.prototype.visitNativeType): Deleted.
(Visitor.prototype.visitNativeTypeInstance): Deleted.
(Visitor.prototype.visitTypeDef): Deleted.
(Visitor.prototype.visitStructType): Deleted.
(Visitor.prototype.visitTypeVariable): Deleted.
(Visitor.prototype.visitConstexprTypeParameter): Deleted.
(Visitor.prototype.visitField): Deleted.
(Visitor.prototype.visitElementalType): Deleted.
(Visitor.prototype.visitReferenceType): Deleted.
(Visitor.prototype.visitPtrType): Deleted.
(Visitor.prototype.visitArrayRefType): Deleted.
(Visitor.prototype.visitArrayType): Deleted.
(Visitor.prototype.visitVariableDecl): Deleted.
(Visitor.prototype.visitAssignment): Deleted.
(Visitor.prototype.visitDereferenceExpression): Deleted.
(Visitor.prototype.visitDotExpression): Deleted.
(Visitor.prototype.visitMakePtrExpression): Deleted.
(Visitor.prototype.visitVariableRef): Deleted.
(Visitor.prototype.visitIfStatement): Deleted.
(Visitor.prototype.visitWhileLoop): Deleted.
(Visitor.prototype.visitDoWhileLoop): Deleted.
(Visitor.prototype.visitForLoop): Deleted.
(Visitor.prototype.visitReturn): Deleted.
(Visitor.prototype.visitContinue): Deleted.
(Visitor.prototype.visitBreak): Deleted.
(Visitor.prototype.visitIntLiteral): Deleted.
(Visitor.prototype.visitIntLiteralType): Deleted.
(Visitor.prototype.visitUintLiteral): Deleted.
(Visitor.prototype.visitNullLiteral): Deleted.
(Visitor.prototype.visitBoolLiteral): Deleted.
(Visitor.prototype.visitNullType): Deleted.
(Visitor.prototype.visitCallExpression): Deleted.
(Visitor.prototype.visitLogicalNot): Deleted.
(Visitor.prototype.visitFunctionLikeBlock): Deleted.
* WebGPUShadingLanguageRI/WrapChecker.js:
(WrapChecker.prototype.visitTypeVariable):
(WrapChecker):

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

25 files changed:
Tools/ChangeLog
Tools/WebGPUShadingLanguageRI/All.js
Tools/WebGPUShadingLanguageRI/AutoWrapper.js
Tools/WebGPUShadingLanguageRI/Checker.js
Tools/WebGPUShadingLanguageRI/ConstexprTypeParameter.js
Tools/WebGPUShadingLanguageRI/FlattenProtocolExtends.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/Func.js
Tools/WebGPUShadingLanguageRI/InferTypesForCall.js
Tools/WebGPUShadingLanguageRI/IntLiteralType.js
Tools/WebGPUShadingLanguageRI/NameResolver.js
Tools/WebGPUShadingLanguageRI/Node.js
Tools/WebGPUShadingLanguageRI/NullType.js
Tools/WebGPUShadingLanguageRI/Parse.js
Tools/WebGPUShadingLanguageRI/Prepare.js
Tools/WebGPUShadingLanguageRI/ProtocolDecl.js
Tools/WebGPUShadingLanguageRI/ProtocolFuncDecl.js
Tools/WebGPUShadingLanguageRI/Rewriter.js
Tools/WebGPUShadingLanguageRI/Test.html
Tools/WebGPUShadingLanguageRI/Test.js
Tools/WebGPUShadingLanguageRI/Type.js
Tools/WebGPUShadingLanguageRI/TypeDefResolver.js
Tools/WebGPUShadingLanguageRI/TypeVariable.js
Tools/WebGPUShadingLanguageRI/UnificationContext.js
Tools/WebGPUShadingLanguageRI/Visitor.js
Tools/WebGPUShadingLanguageRI/WrapChecker.js

index 4d6cfa2..589e9ca 100644 (file)
@@ -1,3 +1,127 @@
+2017-09-11  Filip Pizlo  <fpizlo@apple.com>
+
+        WSL should allow you to say "protocol Foo : Bar { ... }"
+        https://bugs.webkit.org/show_bug.cgi?id=176238
+
+        Reviewed by Mylex Maxfield.
+        
+        This makes protocol "subtyping" work. It's not really subtyping. Protocol A inherits protocol B
+        if every signature in B can be resolved against some signature in A. Also, you can explicitly
+        subtype protocols using extends syntax ("protocol Foo : Bar, Baz { ... }").
+        
+        Also makes the diagnostics when type variable constraints fail a _lot_ better. This will even
+        tell you diagnostics about why a function didn't match when protocols are not inheriting, and
+        then it will tell you why the inheritance failed.
+
+        * WebGPUShadingLanguageRI/All.js:
+        * WebGPUShadingLanguageRI/AutoWrapper.js:
+        (AutoWrapper.prototype.visitTypeVariable):
+        (AutoWrapper):
+        * WebGPUShadingLanguageRI/Checker.js:
+        (Checker.prototype.visitProtocolDecl.set throw):
+        * WebGPUShadingLanguageRI/ConstexprTypeParameter.js:
+        (ConstexprTypeParameter.prototype.verifyAsArgument):
+        (ConstexprTypeParameter.prototype.verifyAsParameter):
+        * WebGPUShadingLanguageRI/FlattenProtocolExtends.js: Added.
+        * WebGPUShadingLanguageRI/Func.js:
+        (Func.prototype.get typeParametersForCallResolution):
+        * WebGPUShadingLanguageRI/InferTypesForCall.js:
+        (inferTypesForCall):
+        * WebGPUShadingLanguageRI/IntLiteralType.js:
+        (IntLiteralType.prototype.verifyAsArgument):
+        * WebGPUShadingLanguageRI/NameResolver.js:
+        (NameResolver.prototype.visitProtocolDecl):
+        (NameResolver.prototype.visitProtocolRef): Deleted.
+        (NameResolver.prototype.visitProtocolFuncDecl): Deleted.
+        (NameResolver.prototype.visitTypeDef): Deleted.
+        (NameResolver.prototype.visitStructType): Deleted.
+        (NameResolver.prototype._resolveTypeArguments): Deleted.
+        (NameResolver.prototype.visitTypeRef): Deleted.
+        (NameResolver.prototype.visitReferenceType): Deleted.
+        (NameResolver.prototype.visitVariableDecl): Deleted.
+        (NameResolver.prototype.visitVariableRef): Deleted.
+        (NameResolver.prototype.visitReturn): Deleted.
+        (NameResolver.prototype.visitCallExpression): Deleted.
+        * WebGPUShadingLanguageRI/Node.js:
+        (Node.prototype.equals):
+        * WebGPUShadingLanguageRI/NullType.js:
+        (NullType.prototype.verifyAsArgument):
+        * WebGPUShadingLanguageRI/Parse.js:
+        (parseProtocolDecl):
+        * WebGPUShadingLanguageRI/Prepare.js:
+        (prepare):
+        * WebGPUShadingLanguageRI/ProtocolDecl.js:
+        (ProtocolDecl): Deleted.
+        (ProtocolDecl.prototype.add): Deleted.
+        (ProtocolDecl.prototype.get signatures): Deleted.
+        (ProtocolDecl.prototype.signaturesByName): Deleted.
+        (ProtocolDecl.prototype.get typeVariable): Deleted.
+        (ProtocolDecl.prototype.signaturesByNameWithTypeVariable): Deleted.
+        (ProtocolDecl.prototype.inherits): Deleted.
+        (ProtocolDecl.prototype.hasHeir): Deleted.
+        (ProtocolDecl.prototype.toString): Deleted.
+        * WebGPUShadingLanguageRI/ProtocolFuncDecl.js:
+        (ProtocolFuncDecl.prototype.get typeParametersForCallResolution):
+        (ProtocolFuncDecl):
+        * WebGPUShadingLanguageRI/Rewriter.js:
+        (Rewriter.prototype.visitProtocolDecl):
+        * WebGPUShadingLanguageRI/Test.html:
+        * WebGPUShadingLanguageRI/Test.js:
+        (TEST_nullTypeVariableUnify):
+        (TEST_simpleProtocolExtends):
+        (TEST_protocolExtendsTwo):
+        * WebGPUShadingLanguageRI/Type.js:
+        (Type.prototype.inherits):
+        * WebGPUShadingLanguageRI/TypeDefResolver.js:
+        (TypeDefResolver.prototype.visitTypeRef):
+        (TypeDefResolver):
+        * WebGPUShadingLanguageRI/TypeVariable.js:
+        (TypeVariable.prototype.inherits):
+        (TypeVariable.prototype.verifyAsArgument):
+        (TypeVariable.prototype.verifyAsParameter):
+        * WebGPUShadingLanguageRI/UnificationContext.js:
+        (UnificationContext.prototype.verify):
+        * WebGPUShadingLanguageRI/Visitor.js:
+        (Visitor.prototype.visitProtocolDecl):
+        (Visitor.prototype.visitTypeRef): Deleted.
+        (Visitor.prototype.visitNativeType): Deleted.
+        (Visitor.prototype.visitNativeTypeInstance): Deleted.
+        (Visitor.prototype.visitTypeDef): Deleted.
+        (Visitor.prototype.visitStructType): Deleted.
+        (Visitor.prototype.visitTypeVariable): Deleted.
+        (Visitor.prototype.visitConstexprTypeParameter): Deleted.
+        (Visitor.prototype.visitField): Deleted.
+        (Visitor.prototype.visitElementalType): Deleted.
+        (Visitor.prototype.visitReferenceType): Deleted.
+        (Visitor.prototype.visitPtrType): Deleted.
+        (Visitor.prototype.visitArrayRefType): Deleted.
+        (Visitor.prototype.visitArrayType): Deleted.
+        (Visitor.prototype.visitVariableDecl): Deleted.
+        (Visitor.prototype.visitAssignment): Deleted.
+        (Visitor.prototype.visitDereferenceExpression): Deleted.
+        (Visitor.prototype.visitDotExpression): Deleted.
+        (Visitor.prototype.visitMakePtrExpression): Deleted.
+        (Visitor.prototype.visitVariableRef): Deleted.
+        (Visitor.prototype.visitIfStatement): Deleted.
+        (Visitor.prototype.visitWhileLoop): Deleted.
+        (Visitor.prototype.visitDoWhileLoop): Deleted.
+        (Visitor.prototype.visitForLoop): Deleted.
+        (Visitor.prototype.visitReturn): Deleted.
+        (Visitor.prototype.visitContinue): Deleted.
+        (Visitor.prototype.visitBreak): Deleted.
+        (Visitor.prototype.visitIntLiteral): Deleted.
+        (Visitor.prototype.visitIntLiteralType): Deleted.
+        (Visitor.prototype.visitUintLiteral): Deleted.
+        (Visitor.prototype.visitNullLiteral): Deleted.
+        (Visitor.prototype.visitBoolLiteral): Deleted.
+        (Visitor.prototype.visitNullType): Deleted.
+        (Visitor.prototype.visitCallExpression): Deleted.
+        (Visitor.prototype.visitLogicalNot): Deleted.
+        (Visitor.prototype.visitFunctionLikeBlock): Deleted.
+        * WebGPUShadingLanguageRI/WrapChecker.js:
+        (WrapChecker.prototype.visitTypeVariable):
+        (WrapChecker):
+
 2017-09-12  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Remove duplicated test results from LayoutTest platform directories
index 74d1fda..5491f6c 100644 (file)
@@ -66,6 +66,7 @@ load("EvaluationCommon.js");
 load("Evaluator.js");
 load("ExpressionFinder.js");
 load("Field.js");
+load("FlattenProtocolExtends.js");
 load("ForLoop.js");
 load("Func.js");
 load("FuncDef.js");
index 78f22e8..a1adcda 100644 (file)
@@ -59,4 +59,9 @@ class AutoWrapper extends Rewriter {
     {
         return TypeRef.wrap(node);
     }
+    
+    visitTypeVariable(node)
+    {
+        return TypeRef.wrap(node);
+    }
 }
index 45ea21f..7a606f3 100644 (file)
@@ -67,7 +67,7 @@ class Checker extends Visitor {
                     throw WTypeError(typeParameter.origin.originString, "Type parameter to protocol signature not inferrable from value parameters");
             }
             if (!set.has(node.typeVariable))
-                throw new WTypeError(signature.origin.originString, "Protocol's type variable not mentioned in signature");
+                throw new WTypeError(signature.origin.originString, "Protocol's type variable (" + node.name + ") not mentioned in signature: " + signature);
         }
     }
     
@@ -77,8 +77,9 @@ class Checker extends Visitor {
             let argumentIsType = typeArguments[i] instanceof Type;
             let result = typeArguments[i].visit(this);
             if (argumentIsType) {
-                if (!typeArguments[i].inherits(typeParameters[i].protocol))
-                    throw new WTypeError(origin.originString, "Type argument does not inherit protocol");
+                let result = typeArguments[i].inherits(typeParameters[i].protocol);
+                if (!result.result)
+                    throw new WTypeError(origin.originString, "Type argument does not inherit protocol: " + result.reason);
             } else {
                 if (!result.equalsWithCommit(typeParameters[i].type))
                     throw new WTypeError(origin.originString, "Wrong type for constexpr");
index 8c23c47..7041064 100644 (file)
@@ -56,12 +56,12 @@ class ConstexprTypeParameter extends Value {
     
     verifyAsArgument(unificationContext)
     {
-        return true;
+        return {result: true};
     }
     
     verifyAsParameter(unificationContext)
     {
-        return true;
+        return {result: true};
     }
     
     toString()
diff --git a/Tools/WebGPUShadingLanguageRI/FlattenProtocolExtends.js b/Tools/WebGPUShadingLanguageRI/FlattenProtocolExtends.js
new file mode 100644 (file)
index 0000000..76172d9
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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 flattenProtocolExtends(program)
+{
+    let visiting = new VisitingSet();
+    
+    function flatten(protocol)
+    {
+        if (!protocol.extends.length)
+            return;
+        
+        visiting.doVisit(protocol, () => {
+            for (let parent of protocol.extends) {
+                parent = parent.protocolDecl;
+                flatten(parent);
+                for (let signature of parent.signatures) {
+                    let newSignature = signature.visit(
+                        new Substitution([parent.typeVariable], [protocol.typeVariable]));
+                    protocol.add(newSignature);
+                }
+            }
+            protocol.extends = [];
+        });
+    }
+    
+    for (let protocol of program.protocols.values())
+        flatten(protocol);
+}
+
index 08d4d5b..69bb28c 100644 (file)
@@ -42,6 +42,7 @@ class Func extends Node {
     get name() { return this._name; }
     get returnType() { return this._returnType; }
     get typeParameters() { return this._typeParameters; }
+    get typeParametersForCallResolution() { return this.typeParameters; }
     get parameters() { return this._parameters; }
     get parameterTypes() { return this.parameters.map(parameter => parameter.type); }
     get isCast() { return this._isCast; }
index 55feec7..7b17996 100644 (file)
@@ -30,7 +30,7 @@ function inferTypesForCall(func, typeArguments, argumentTypes, returnType)
         return {failure: new OverloadResolutionFailure(func, "Wrong number of type arguments (passed " + typeArguments.length + ", require " + func.typeParameters.length + ")")};
     if (argumentTypes.length != func.parameters.length)
         return {failure: new OverloadResolutionFailure(func, "Wrong number of arguments (passed " + argumentTypes.length + ", require " + func.parameters.length + ")")};
-    let unificationContext = new UnificationContext(func.typeParameters);
+    let unificationContext = new UnificationContext(func.typeParametersForCallResolution);
     for (let i = 0; i < typeArguments.length; ++i) {
         let argument = typeArguments[i];
         let parameter = func.typeParameters[i];
@@ -45,8 +45,9 @@ function inferTypesForCall(func, typeArguments, argumentTypes, returnType)
     }
     if (returnType && !returnType.unify(unificationContext, func.returnType))
         return {failure: new OverloadResolutionFailure(func, "Return type " + func.returnType + " does not match " + returnType)};
-    if (!unificationContext.verify())
-        return {failure: new OverloadResolutionFailure(func, "Violates type variable constraints")};
+    let verificationResult = unificationContext.verify();
+    if (!verificationResult.result)
+        return {failure: new OverloadResolutionFailure(func, verificationResult.reason)};
     let shouldBuildTypeArguments = !typeArguments.length;
     if (shouldBuildTypeArguments)
         typeArguments = [];
index 6948ff2..12bf64b 100644 (file)
@@ -55,7 +55,11 @@ class IntLiteralType extends Type {
     verifyAsArgument(unificationContext)
     {
         let realThis = unificationContext.find(this);
-        return realThis.isNumber && realThis.canRepresent(this.value);
+        if (!realThis.isNumber)
+            return {result: false, reason: "Cannot use int literal with non-number type " + realThis};
+        if (!realThis.canRepresent(this.value))
+            return {result: false, reason: "Int literal " + this.value + " too large to be represented by type " + realThis};
+        return {result: true};
     }
     
     verifyAsParameter(unificationContext)
index cbf07ee..c070030 100644 (file)
@@ -129,6 +129,8 @@ class NameResolver extends Visitor {
     
     visitProtocolDecl(node)
     {
+        for (let parent of node.extends)
+            parent.visit(this);
         let nameContext = new NameContext(this._nameContext);
         nameContext.add(node.typeVariable);
         let checker = new NameResolver(nameContext);
index aa8dfaf..ab43b82 100644 (file)
@@ -84,7 +84,7 @@ class Node {
     equals(other)
     {
         let unificationContext = new UnificationContext();
-        if (this.unify(unificationContext, other) && unificationContext.verify())
+        if (this.unify(unificationContext, other) && unificationContext.verify().result)
             return unificationContext;
         return false;
     }
index dae3cda..5bd30c1 100644 (file)
@@ -56,7 +56,9 @@ class NullType extends Type {
     verifyAsArgument(unificationContext)
     {
         let realThis = unificationContext.find(this);
-        return realThis.isPtr || realThis.isArrayRef;
+        if (realThis.isPtr || realThis.isArrayRef)
+            return {result: true};
+        return {result: false, reason: "Null cannot be used with non-pointer type " + realThis};
     }
     
     verifyAsParameter(unificationContext)
index c7446d1..17d46d4 100644 (file)
@@ -747,10 +747,15 @@ function parse(program, origin, originKind, lineNumberOffset, text)
     {
         let origin = consume("protocol");
         let name = consumeKind("identifier").text;
-        // FIXME: Support protocol inclusion
-        // https://bugs.webkit.org/show_bug.cgi?id=176238
-        consume("{");
         let result = new ProtocolDecl(origin, name);
+        if (tryConsume(":")) {
+            while (!test("{")) {
+                result.addExtends(parseProtocolRef());
+                if (!tryConsume(","))
+                    break;
+            }
+        }
+        consume("{");
         while (!tryConsume("}")) {
             result.add(parseProtocolFuncDecl());
             consume(";");
index a36a6e5..d3537d4 100644 (file)
@@ -31,6 +31,7 @@ function prepare(origin, lineNumberOffset, text)
     parse(program, origin, "user", lineNumberOffset, text);
     resolveNames(program);
     resolveTypeDefs(program);
+    flattenProtocolExtends(program);
     check(program);
     checkLiteralTypes(program);
     checkProgramWrapped(program);
index 863531d..86b6183 100644 (file)
@@ -28,12 +28,18 @@ class ProtocolDecl extends Protocol {
     constructor(origin, name)
     {
         super(origin, name);
+        this.extends = [];
         this._signatures = [];
         this._signatureMap = new Map();
         this._typeVariable = new TypeVariable(origin, name, null);
         this.isPrimitive = false;
     }
     
+    addExtends(protocol)
+    {
+        this.extends.push(protocol);
+    }
+    
     add(signature)
     {
         if (!(signature instanceof ProtocolFuncDecl))
@@ -63,7 +69,7 @@ class ProtocolDecl extends Protocol {
     inherits(otherProtocol)
     {
         if (!otherProtocol)
-            return true;
+            return {result: true};
         
         if (otherProtocol instanceof ProtocolRef)
             otherProtocol = otherProtocol.protocolDecl;
@@ -71,17 +77,20 @@ class ProtocolDecl extends Protocol {
         for (let otherSignature of otherProtocol.signatures) {
             let signatures = this.signaturesByName(otherSignature.name);
             if (!signatures)
-                return false;
-            let overload = resolveOverloadImpl(signatures, [], otherSignature.parameterTypes, otherSignature.returnTypeForOverloadResolution);
+                return {result: false, reason: "Protocol " + this.name + " does not have a function named " + otherSignature.name + " (looking at signature " + otherSignature + ")"};
+            let overload = resolveOverloadImpl(
+                signatures, [],
+                otherSignature.parameterTypes,
+                otherSignature.returnTypeForOverloadResolution);
             if (!overload.func)
-                return false;
+                return {result: false, reason: "Did not find matching signature for " + otherSignature + " in " + this.name + (overload.failures.length ? " (tried: " + overload.failures.join("; ") + ")" : "")};
             let substitutedReturnType =
                 overload.func.returnType.substituteToUnification(
                     overload.func.typeParameters, overload.unificationContext);
             if (!substitutedReturnType.equals(otherSignature.returnType))
-                return false;
+                return {result: false, reason: "Return type mismatch between " + otherSignature.returnType + " and " + substitutedReturnType};
         }
-        return true;
+        return {result: true};
     }
     
     hasHeir(type)
@@ -92,14 +101,14 @@ class ProtocolDecl extends Protocol {
             signature = signature.visit(substitution);
             let overload = resolveOverloadImpl(signature.possibleOverloads, signature.typeParameters, signature.parameterTypes, signature.returnTyupeForOverloadResolution);
             if (!overload.func)
-                return false;
+                return {result: false, reason: "Did not find matching signature for " + signature + " 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 false;
+                return {result: false, reason: "Return type mismatch between " + signature.returnType + " and " + substitutedReturnType};
         }
-        return true;
+        return {result: true};
     }
     
     toString()
index 76da053..89b5c9b 100644 (file)
  */
 "use strict";
 
-class ProtocolFuncDecl extends Func { }
+class ProtocolFuncDecl extends Func {
+    get typeParametersForCallResolution()
+    {
+        return this.typeParameters.concat(this.protocolDecl.typeVariable);
+    }
+}
 
index 51cd7ea..4469207 100644 (file)
@@ -59,6 +59,7 @@ class Rewriter extends VisitorBase {
     visitTypeDef(node) { return node; }
     visitStructType(node) { return node; }
     visitConstexprTypeParameter(node) { return node; }
+    visitProtocolDecl(node) { return node; }
 
     // This is almost wrong. We instantiate Func in Substitution in ProtocolDecl. Then, we end up
     // not rewriting type variables. I think that just works because not rewriting them there is OK.
index 41fef66..da079ad 100644 (file)
@@ -42,6 +42,7 @@
 <script src="Evaluator.js"></script>
 <script src="ExpressionFinder.js"></script>
 <script src="Field.js"></script>
+<script src="FlattenProtocolExtends.js"></script>
 <script src="ForLoop.js"></script>
 <script src="Func.js"></script>
 <script src="FuncDef.js"></script>
index 1a1cca2..a742f00 100644 (file)
@@ -972,7 +972,7 @@ function TEST_nullTypeVariableUnify()
                 if (!result)
                     throw new Error("In order " + order + " cannot unify " + left + " with " + right);
             }
-            if (!unificationContext.verify())
+            if (!unificationContext.verify().result)
                 throw new Error("In order " + order.map(value => "(" + value + ")") + " cannot verify");
         });
 }
@@ -1977,6 +1977,88 @@ function TEST_paramChainStructDevice()
     checkInt(program, callFunction(program, "bar", [], [TypedValue.box(new PtrType(null, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 79201);
 }
 
+function TEST_simpleProtocolExtends()
+{
+    let program = doPrep(`
+        protocol Foo {
+            void foo(thread Foo^);
+        }
+        protocol Bar : Foo {
+            void bar(thread Bar^);
+        }
+        void fuzz<T:Foo>(thread T^ p)
+        {
+            foo(p);
+        }
+        void buzz<T:Bar>(thread T^ p)
+        {
+            fuzz(p);
+            bar(p);
+        }
+        void foo(thread int^ p)
+        {
+            ^p = ^p + 743;
+        }
+        void bar(thread int^ p)
+        {
+            ^p = ^p + 91;
+        }
+        int thingy(int a)
+        {
+            buzz(&a);
+            return a;
+        }
+    `);
+    checkInt(program, callFunction(program, "thingy", [], [makeInt(program, 642)]), 642 + 743 + 91);
+}
+
+function TEST_protocolExtendsTwo()
+{
+    let program = doPrep(`
+        protocol Foo {
+            void foo(thread Foo^);
+        }
+        protocol Bar {
+            void bar(thread Bar^);
+        }
+        protocol Baz : Foo, Bar {
+            void baz(thread Baz^);
+        }
+        void fuzz<T:Foo>(thread T^ p)
+        {
+            foo(p);
+        }
+        void buzz<T:Bar>(thread T^ p)
+        {
+            bar(p);
+        }
+        void xuzz<T:Baz>(thread T^ p)
+        {
+            fuzz(p);
+            buzz(p);
+            baz(p);
+        }
+        void foo(thread int^ p)
+        {
+            ^p = ^p + 743;
+        }
+        void bar(thread int^ p)
+        {
+            ^p = ^p + 91;
+        }
+        void baz(thread int^ p)
+        {
+            ^p = ^p + 39;
+        }
+        int thingy(int a)
+        {
+            xuzz(&a);
+            return a;
+        }
+    `);
+    checkInt(program, callFunction(program, "thingy", [], [makeInt(program, 642)]), 642 + 743 + 91 + 39);
+}
+
 let filter = /.*/; // run everything by default
 if (this["arguments"]) {
     for (let i = 0; i < arguments.length; i++) {
index 6d730b1..12e1051 100644 (file)
@@ -36,7 +36,7 @@ class Type extends Node {
     inherits(protocol)
     {
         if (!protocol)
-            return true;
+            return {result: true};
         return protocol.hasHeir(this);
     }
     
index 1da15fd..04702f3 100644 (file)
@@ -41,8 +41,9 @@ class TypeDefResolver extends Visitor {
                     throw new Error("argument/parameter mismatch (should have been caught earlier)");
                 for (let i = 0; i < node.typeArguments.length; ++i)
                     node.typeArguments[i].unify(unificationContext, node.type.typeParameters[i]);
-                if (!unificationContext.verify())
-                    throw new WTypeError(node.origin.originString, "Type reference to a type definition violates protocol constraints");
+                let verificationResult = unificationContext.verify();
+                if (!verificationResult.result)
+                    throw new WTypeError(node.origin.originString, "Type reference to a type definition violates protocol constraints: " + verificationResult.reason);
                 
                 let newType = node.type.type.substituteToUnification(node.type.typeParameters, unificationContext);
                 newType.visit(this);
index 7cec089..33218a9 100644 (file)
@@ -47,9 +47,9 @@ class TypeVariable extends Type {
     inherits(protocol)
     {
         if (!protocol)
-            return true;
+            return {result: true};
         if (!this.protocol)
-            return false;
+            return {result: false, reason: "Type variable " + this + " does not have a protocol"};
         return this.protocol.inherits(protocol);
     }
     
@@ -73,19 +73,29 @@ class TypeVariable extends Type {
         // The thing we get unified with must be a type variable that accepts a broader set of
         // things than we do.
         if (!(realThis instanceof TypeVariable))
-            return false;
+            return {result: false, reason: "Type variable argument " + this + " cannot be passed to non-type-variable parameter type " + realThis};
         
-        if (!this.protocol)
-            return !realThis.protocol;
+        if (!this.protocol) {
+            if (realThis.protocol)
+                return {result: false, reason: "Type variable without protocol " + this + " cannot be passed to parameter type variable with protocol " + realThis.protocol};
+            return {result: true};
+        }
         
-        return this.protocol.inherits(realThis.protocol);
+        let result = this.protocol.inherits(realThis.protocol);
+        if (!result.result)
+            return {result: false, reason: "Protocol " + this.protocol + " does not subsume protocol " + realThis.protocol + " (passing type " + this + " to type " + realThis + "): " + result.reason};
+        return {result: true};
     }
     
     verifyAsParameter(unificationContext)
     {
         if (!this.protocol)
-            return true;
-        return unificationContext.find(this).inherits(this.protocol);
+            return {result: true};
+        let realThis = unificationContext.find(this);
+        let result = realThis.inherits(this.protocol);
+        if (!result.result)
+            return {result: false, reason: "Type " + realThis + " does not inherit protocol " + this.protocol + " (passing type " + realThis + " to type " + this + "): " + result.reason};
+        return {result: true};
     }
     
     toString()
index 43ac533..7a73265 100644 (file)
@@ -92,18 +92,22 @@ class UnificationContext {
     verify()
     {
         for (let typeParameter of this._typeParameters) {
-            if (!typeParameter.verifyAsParameter(this))
-                return false;
+            let result = typeParameter.verifyAsParameter(this);
+            if (!result.result)
+                return result;
         }
         let numTypeVariableArguments = 0;
         let argumentSet = new Set();
         for (let typeArgument of this.typeArguments()) {
-            if (!typeArgument.verifyAsArgument(this))
-                return false;
+            let result = typeArgument.verifyAsArgument(this);
+            if (!result.result)
+                return result;
             argumentSet.add(this.find(typeArgument));
             numTypeVariableArguments++;
         }
-        return argumentSet.size == numTypeVariableArguments;
+        if (argumentSet.size == numTypeVariableArguments)
+            return {result: true};
+        return {result: false, reason: "Type variables used as arguments got unified"};
     }
     
     get conversionCost()
index a6b9141..92ca0f2 100644 (file)
@@ -90,6 +90,8 @@ class Visitor extends VisitorBase {
     
     visitProtocolDecl(node)
     {
+        for (let protocol of node.extends)
+            protocol.visit(this);
         for (let signature of node.signatures)
             signature.visit(this);
     }
index 2c786a0..4cadc7d 100644 (file)
@@ -77,6 +77,11 @@ class WrapChecker extends Visitor {
         this._foundUnwrapped(node);
     }
     
+    visitTypeVariable(node)
+    {
+        this._foundUnwrapped(node);
+    }
+    
     // NOTE: This does not know how to handle NativeTypeInstance, because this is never called on instantiated
     // code. Once code is instantiated, you cannot instantiate it further.