blob: a62415e6e777a3218f2845883565cc3483df2d24 [file] [log] [blame]
/*
* 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 doPrep(code)
{
return prepare("/internal/test", 0, code);
}
function doLex(code)
{
let lexer = new Lexer("/internal/test", "native", 0, code);
var result = [];
for (;;) {
let next = lexer.next();
if (!next)
return result;
result.push(next);
}
return result;
}
function makeInt(program, value)
{
return TypedValue.box(program.intrinsics.int32, value);
}
function makeUint(program, value)
{
return TypedValue.box(program.intrinsics.uint32, value);
}
function makeUint8(program, value)
{
return TypedValue.box(program.intrinsics.uint8, value);
}
function makeBool(program, value)
{
return TypedValue.box(program.intrinsics.bool, value);
}
function makeFloat(program, value)
{
return TypedValue.box(program.intrinsics.float, value);
}
function makeDouble(program, value)
{
return TypedValue.box(program.intrinsics.double, value);
}
function makeEnum(program, enumName, value)
{
let enumType = program.types.get(enumName);
if (!enumType)
throw new Error("No type named " + enumName);
let enumMember = enumType.memberByName(value);
if (!enumMember)
throw new Error("No member named " + enumMember + " in " + enumType);
return TypedValue.box(enumType, enumMember.value.unifyNode.valueForSelectedType);
}
function checkNumber(program, result, expected)
{
if (!result.type.unifyNode.isNumber)
throw new Error("Wrong result type; result: " + result);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkInt(program, result, expected)
{
if (!result.type.equals(program.intrinsics.int32))
throw new Error("Wrong result type; result: " + result);
checkNumber(program, result, expected);
}
function checkEnum(program, result, expected)
{
if (!(result.type.unifyNode instanceof EnumType))
throw new Error("Wrong result type; result: " + result);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkUint(program, result, expected)
{
if (!result.type.equals(program.intrinsics.uint32))
throw new Error("Wrong result type: " + result.type);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkUint8(program, result, expected)
{
if (!result.type.equals(program.intrinsics.uint8))
throw new Error("Wrong result type: " + result.type);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkBool(program, result, expected)
{
if (!result.type.equals(program.intrinsics.bool))
throw new Error("Wrong result type: " + result.type);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkFloat(program, result, expected)
{
if (!result.type.equals(program.intrinsics.float))
throw new Error("Wrong result type: " + result.type);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkDouble(program, result, expected)
{
if (!result.type.equals(program.intrinsics.double))
throw new Error("Wrong result type: " + result.type);
if (result.value != expected)
throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
}
function checkLexerToken(result, expectedIndex, expectedKind, expectedText)
{
if (result._index != expectedIndex)
throw new Error("Wrong lexer index; result: " + result._index + " (expected " + expectedIndex + ")");
if (result._kind != expectedKind)
throw new Error("Wrong lexer kind; result: " + result._kind + " (expected " + expectedKind + ")");
if (result._text != expectedText)
throw new Error("Wrong lexer text; result: " + result._text + " (expected " + expectedText + ")");
}
function checkFail(callback, predicate)
{
try {
callback();
throw new Error("Did not throw exception");
} catch (e) {
if (predicate(e)) {
return;
}
throw e;
}
}
let okToTest = false;
let tests = new Proxy({}, {
set(target, property, value, receiver)
{
if (property in target)
throw new Error("Trying to declare duplicate test: " + property);
target[property] = value;
return true;
}
});
tests.literalBool = function() {
let program = doPrep("bool foo() { return true; }");
checkBool(program, callFunction(program, "foo", [], []), true);
}
tests.identityBool = function() {
let program = doPrep("bool foo(bool x) { return x; }");
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true)]), true);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false)]), false);
}
tests.intSimpleMath = function() {
let program = doPrep("int foo(int x, int y) { return x + y; }");
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), 12);
program = doPrep("int foo(int x, int y) { return x - y; }");
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), 2);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5), makeInt(program, 7)]), -2);
program = doPrep("int foo(int x, int y) { return x * y; }");
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), 35);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, -5)]), -35);
program = doPrep("int foo(int x, int y) { return x / y; }");
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 2)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, -2)]), -3);
}
tests.uintSimpleMath = function() {
let program = doPrep("uint foo(uint x, uint y) { return x + y; }");
checkUint(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), 12);
program = doPrep("uint foo(uint x, uint y) { return x - y; }");
checkUint(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), 2);
checkUint(program, callFunction(program, "foo", [], [makeUint(program, 5), makeUint(program, 7)]), 4294967294);
program = doPrep("uint foo(uint x, uint y) { return x * y; }");
checkUint(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), 35);
program = doPrep("uint foo(uint x, uint y) { return x / y; }");
checkUint(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 2)]), 3);
}
tests.uint8SimpleMath = function() {
let program = doPrep("uint8 foo(uint8 x, uint8 y) { return x + y; }");
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), 12);
program = doPrep("uint8 foo(uint8 x, uint8 y) { return x - y; }");
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), 2);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 5), makeUint8(program, 7)]), 254);
program = doPrep("uint8 foo(uint8 x, uint8 y) { return x * y; }");
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), 35);
program = doPrep("uint8 foo(uint8 x, uint8 y) { return x / y; }");
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 2)]), 3);
}
tests.equality = function() {
let program = doPrep("bool foo(uint x, uint y) { return x == y; }");
checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), false);
checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 7)]), true);
program = doPrep("bool foo(uint8 x, uint8 y) { return x == y; }");
checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), false);
checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 7)]), true);
program = doPrep("bool foo(int x, int y) { return x == y; }");
checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), false);
checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 7)]), true);
program = doPrep("bool foo(bool x, bool y) { return x == y; }");
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, true)]), false);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, false)]), false);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, false)]), true);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, true)]), true);
}
tests.logicalNegation = function()
{
let program = doPrep("bool foo(bool x) { return !x; }");
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true)]), false);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false)]), true);
}
tests.notEquality = function() {
let program = doPrep("bool foo(uint x, uint y) { return x != y; }");
checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), true);
checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 7)]), false);
program = doPrep("bool foo(uint8 x, uint8 y) { return x != y; }");
checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), true);
checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 7)]), false);
program = doPrep("bool foo(int x, int y) { return x != y; }");
checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), true);
checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 7)]), false);
program = doPrep("bool foo(bool x, bool y) { return x != y; }");
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, true)]), true);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, false)]), true);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false), makeBool(program, false)]), false);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true), makeBool(program, true)]), false);
}
tests.equalityTypeFailure = function()
{
checkFail(
() => doPrep("bool foo(int x, uint y) { return x == y; }"),
(e) => e instanceof WTypeError && e.message.indexOf("/internal/test:1") != -1);
}
tests.generalNegation = function()
{
let program = doPrep("bool foo(int x) { return !x; }");
checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7)]), false);
checkBool(program, callFunction(program, "foo", [], [makeInt(program, 0)]), true);
}
tests.add1 = function() {
let program = doPrep("int foo(int x) { return x + 1; }");
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 42)]), 43);
}
tests.simpleGeneric = function() {
let program = doPrep(`
T id<T>(T x) { return x; }
int foo(int x) { return id(x) + 1; }
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 42)]), 43);
}
tests.nameResolutionFailure = function()
{
checkFail(
() => doPrep("int foo(int x) { return x + y; }"),
(e) => e instanceof WTypeError && e.message.indexOf("/internal/test:1") != -1);
}
tests.simpleVariable = function()
{
let program = doPrep(`
int foo(int p)
{
int result = p;
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 42)]), 42);
}
tests.simpleAssignment = function()
{
let program = doPrep(`
int foo(int p)
{
int result;
result = p;
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 42)]), 42);
}
tests.simpleDefault = function()
{
let program = doPrep(`
int foo()
{
int result;
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 0);
}
tests.simpleDereference = function()
{
let program = doPrep(`
int foo(device int* p)
{
return *p;
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 13);
checkInt(program, callFunction(program, "foo", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 13);
}
tests.dereferenceStore = function()
{
let program = doPrep(`
void foo(device int* p)
{
*p = 52;
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 13);
callFunction(program, "foo", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]);
if (buffer.get(0) != 52)
throw new Error("Expected buffer to contain 52 but it contains: " + buffer.get(0));
}
tests.simpleMakePtr = function()
{
let program = doPrep(`
thread int* foo()
{
int x = 42;
return &x;
}
`);
let result = callFunction(program, "foo", [], []);
if (!result.type.isPtr)
throw new Error("Return type is not a pointer: " + result.type);
if (!result.type.elementType.equals(program.intrinsics.int32))
throw new Error("Return type is not a pointer to an int: " + result.type);
if (!(result.value instanceof EPtr))
throw new Error("Return value is not an EPtr: " + result.value);
let value = result.value.loadValue();
if (value != 42)
throw new Error("Expected 42 but got: " + value);
}
tests.threadArrayLoad = function()
{
let program = doPrep(`
int foo(thread int[] array)
{
return array[0u];
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 89);
let result = callFunction(program, "foo", [], [TypedValue.box(new ArrayRefType(externalOrigin, "thread", program.intrinsics.int32), new EArrayRef(new EPtr(buffer, 0), 1))]);
checkInt(program, result, 89);
}
tests.threadArrayLoadIntLiteral = function()
{
let program = doPrep(`
int foo(thread int[] array)
{
return array[0];
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 89);
let result = callFunction(program, "foo", [], [TypedValue.box(new ArrayRefType(externalOrigin, "thread", program.intrinsics.int32), new EArrayRef(new EPtr(buffer, 0), 1))]);
checkInt(program, result, 89);
}
tests.deviceArrayLoad = function()
{
let program = doPrep(`
int foo(device int[] array)
{
return array[0u];
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 89);
let result = callFunction(program, "foo", [], [TypedValue.box(new ArrayRefType(externalOrigin, "device", program.intrinsics.int32), new EArrayRef(new EPtr(buffer, 0), 1))]);
checkInt(program, result, 89);
}
tests.threadArrayStore = function()
{
let program = doPrep(`
void foo(thread int[] array, int value)
{
array[0u] = value;
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 15);
let arrayRef = TypedValue.box(
new ArrayRefType(externalOrigin, "thread", program.intrinsics.int32),
new EArrayRef(new EPtr(buffer, 0), 1));
callFunction(program, "foo", [], [arrayRef, makeInt(program, 65)]);
if (buffer.get(0) != 65)
throw new Error("Bad value stored into buffer (expected 65): " + buffer.get(0));
callFunction(program, "foo", [], [arrayRef, makeInt(program, -111)]);
if (buffer.get(0) != -111)
throw new Error("Bad value stored into buffer (expected -111): " + buffer.get(0));
}
tests.deviceArrayStore = function()
{
let program = doPrep(`
void foo(device int[] array, int value)
{
array[0u] = value;
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 15);
let arrayRef = TypedValue.box(
new ArrayRefType(externalOrigin, "device", program.intrinsics.int32),
new EArrayRef(new EPtr(buffer, 0), 1));
callFunction(program, "foo", [], [arrayRef, makeInt(program, 65)]);
if (buffer.get(0) != 65)
throw new Error("Bad value stored into buffer (expected 65): " + buffer.get(0));
callFunction(program, "foo", [], [arrayRef, makeInt(program, -111)]);
if (buffer.get(0) != -111)
throw new Error("Bad value stored into buffer (expected -111): " + buffer.get(0));
}
tests.deviceArrayStoreIntLiteral = function()
{
let program = doPrep(`
void foo(device int[] array, int value)
{
array[0] = value;
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 15);
let arrayRef = TypedValue.box(
new ArrayRefType(externalOrigin, "device", program.intrinsics.int32),
new EArrayRef(new EPtr(buffer, 0), 1));
callFunction(program, "foo", [], [arrayRef, makeInt(program, 65)]);
if (buffer.get(0) != 65)
throw new Error("Bad value stored into buffer (expected 65): " + buffer.get(0));
callFunction(program, "foo", [], [arrayRef, makeInt(program, -111)]);
if (buffer.get(0) != -111)
throw new Error("Bad value stored into buffer (expected -111): " + buffer.get(0));
}
tests.simpleProtocol = function()
{
let program = doPrep(`
protocol MyAddable {
MyAddable operator+(MyAddable, MyAddable);
}
T add<T:MyAddable>(T a, T b)
{
return a + b;
}
int foo(int x)
{
return add(x, 73);
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 45)]), 45 + 73);
}
tests.typeMismatchReturn = function()
{
checkFail(
() => doPrep(`
int foo()
{
return 0u;
}
`),
(e) => e instanceof WTypeError);
}
tests.typeMismatchVariableDecl = function()
{
checkFail(
() => doPrep(`
void foo(uint x)
{
int y = x;
}
`),
(e) => e instanceof WTypeError);
}
tests.typeMismatchAssignment = function()
{
checkFail(
() => doPrep(`
void foo(uint x)
{
int y;
y = x;
}
`),
(e) => e instanceof WTypeError);
}
tests.typeMismatchReturnParam = function()
{
checkFail(
() => doPrep(`
int foo(uint x)
{
return x;
}
`),
(e) => e instanceof WTypeError);
}
tests.badAdd = function()
{
checkFail(
() => doPrep(`
void bar<T>(T) { }
void foo(int x, uint y)
{
bar(x + y);
}
`),
(e) => e instanceof WTypeError && e.message.indexOf("native int32 operator+<>(int32,int32)") != -1);
}
tests.lexerKeyword = function()
{
let result = doLex("ident for while 123 123u { } {asd asd{ 1a3 1.2 + 3.4 + 1. + .2 1.2d 0.d .3d && ||");
if (result.length != 25)
throw new Error("Lexer emitted an incorrect number of tokens (expected 23): " + result.length);
checkLexerToken(result[0], 0, "identifier", "ident");
checkLexerToken(result[1], 6, "keyword", "for");
checkLexerToken(result[2], 10, "keyword", "while");
checkLexerToken(result[3], 16, "intLiteral", "123");
checkLexerToken(result[4], 20, "uintLiteral", "123u");
checkLexerToken(result[5], 25, "punctuation", "{");
checkLexerToken(result[6], 27, "punctuation", "}");
checkLexerToken(result[7], 29, "punctuation", "{");
checkLexerToken(result[8], 30, "identifier", "asd");
checkLexerToken(result[9], 34, "identifier", "asd");
checkLexerToken(result[10], 37, "punctuation", "{");
checkLexerToken(result[11], 39, "intLiteral", "1");
checkLexerToken(result[12], 40, "identifier", "a3");
checkLexerToken(result[13], 43, "floatLiteral", "1.2");
checkLexerToken(result[14], 47, "punctuation", "+");
checkLexerToken(result[15], 49, "floatLiteral", "3.4");
checkLexerToken(result[16], 53, "punctuation", "+");
checkLexerToken(result[17], 55, "floatLiteral", "1.");
checkLexerToken(result[18], 58, "punctuation", "+");
checkLexerToken(result[19], 60, "floatLiteral", ".2");
checkLexerToken(result[20], 63, "floatLiteral", "1.2d");
checkLexerToken(result[21], 68, "floatLiteral", "0.d");
checkLexerToken(result[22], 72, "floatLiteral", ".3d");
checkLexerToken(result[23], 76, "punctuation", "&&");
checkLexerToken(result[24], 79, "punctuation", "||");
}
tests.simpleNoReturn = function()
{
checkFail(
() => doPrep("int foo() { }"),
(e) => e instanceof WTypeError);
}
tests.simpleUnreachableCode = function()
{
checkFail(
() => doPrep(`
void foo()
{
return;
int x;
}
`),
(e) => e instanceof WTypeError);
}
tests.simpleStruct = function()
{
let program = doPrep(`
struct Foo {
int x;
int y;
}
Foo foo(Foo foo)
{
Foo result;
result.x = foo.y;
result.y = foo.x;
return result;
}
`);
let structType = program.types.get("Foo");
if (!structType)
throw new Error("Did not find Foo type");
let buffer = new EBuffer(2);
buffer.set(0, 62);
buffer.set(1, 24);
let result = callFunction(program, "foo", [], [new TypedValue(structType, new EPtr(buffer, 0))]);
if (!result.type.equals(structType))
throw new Error("Wrong result type: " + result.type);
let x = result.ePtr.get(0);
let y = result.ePtr.get(1);
if (x != 24)
throw new Error("Wrong result for x: " + x + " (y = " + y + ")");
if (y != 62)
throw new Error("Wrong result for y: " + y + " (x + " + x + ")");
}
tests.genericStructInstance = function()
{
let program = doPrep(`
struct Foo<T> {
T x;
T y;
}
Foo<int> foo(Foo<int> foo)
{
Foo<int> result;
result.x = foo.y;
result.y = foo.x;
return result;
}
`);
let structType = TypeRef.instantiate(program.types.get("Foo"), [program.intrinsics.int32]);
let buffer = new EBuffer(2);
buffer.set(0, 62);
buffer.set(1, 24);
let result = callFunction(program, "foo", [], [new TypedValue(structType, new EPtr(buffer, 0))]);
let x = result.ePtr.get(0);
let y = result.ePtr.get(1);
if (x != 24)
throw new Error("Wrong result for x: " + x + " (y = " + y + ")");
if (y != 62)
throw new Error("Wrong result for y: " + y + " (x + " + x + ")");
}
tests.doubleGenericCallsDoubleGeneric = function()
{
doPrep(`
void foo<T, U>(T, U) { }
void bar<V, W>(V x, W y) { foo(x, y); }
`);
}
tests.doubleGenericCallsSingleGeneric = function()
{
checkFail(
() => doPrep(`
void foo<T>(T, T) { }
void bar<V, W>(V x, W y) { foo(x, y); }
`),
(e) => e instanceof WTypeError);
}
tests.loadNull = function()
{
checkFail(
() => doPrep(`
void sink<T>(T) { }
void foo() { sink(*null); }
`),
(e) => e instanceof WTypeError && e.message.indexOf("Type passed to dereference is not a pointer: null") != -1);
}
tests.storeNull = function()
{
checkFail(
() => doPrep(`
void foo() { *null = 42; }
`),
(e) => e instanceof WTypeError && e.message.indexOf("Type passed to dereference is not a pointer: null") != -1);
}
tests.returnNull = function()
{
let program = doPrep(`
thread int* foo() { return null; }
`);
let result = callFunction(program, "foo", [], []);
if (!result.type.isPtr)
throw new Error("Return type is not a pointer: " + result.type);
if (!result.type.elementType.equals(program.intrinsics.int32))
throw new Error("Return type is not a pointer to an int: " + result.type);
if (result.value != null)
throw new Error("Return value is not null: " + result.value);
}
tests.dereferenceDefaultNull = function()
{
let program = doPrep(`
int foo()
{
thread int* p;
return *p;
}
`);
checkFail(
() => callFunction(program, "foo", [], []),
(e) => e instanceof WTrapError);
}
tests.defaultInitializedNull = function()
{
let program = doPrep(`
int foo()
{
thread int* p = null;;
return *p;
}
`);
checkFail(
() => callFunction(program, "foo", [], []),
(e) => e instanceof WTrapError);
}
tests.passNullToPtrMonomorphic = function()
{
let program = doPrep(`
int foo(thread int* ptr)
{
return *ptr;
}
int bar()
{
return foo(null);
}
`);
checkFail(
() => callFunction(program, "bar", [], []),
(e) => e instanceof WTrapError);
}
tests.passNullToPtrPolymorphic = function()
{
checkFail(
() => doPrep(`
T foo<T>(thread T* ptr)
{
return *ptr;
}
int bar()
{
return foo(null);
}
`),
(e) => e instanceof WTypeError);
}
tests.passNullToPolymorphic = function()
{
checkFail(
() => doPrep(`
T foo<T>(T ptr)
{
return ptr;
}
int bar()
{
return foo(null);
}
`),
(e) => e instanceof WTypeError);
}
tests.loadNullArrayRef = function()
{
checkFail(
() => doPrep(`
void sink<T>(T) { }
void foo() { sink(null[0u]); }
`),
(e) => e instanceof WTypeError && e.message.indexOf("Cannot resolve access") != -1);
}
tests.storeNullArrayRef = function()
{
checkFail(
() => doPrep(`
void foo() { null[0u] = 42; }
`),
(e) => e instanceof WTypeError && e.message.indexOf("Cannot resolve access") != -1);
}
tests.returnNullArrayRef = function()
{
let program = doPrep(`
thread int[] foo() { return null; }
`);
let result = callFunction(program, "foo", [], []);
if (!result.type.isArrayRef)
throw new Error("Return type is not an array reference: " + result.type);
if (!result.type.elementType.equals(program.intrinsics.int32))
throw new Error("Return type is not an int array reference: " + result.type);
if (result.value != null)
throw new Error("Return value is not null: " + result.value);
}
tests.dereferenceDefaultNullArrayRef = function()
{
let program = doPrep(`
int foo()
{
thread int[] p;
return p[0u];
}
`);
checkFail(
() => callFunction(program, "foo", [], []),
(e) => e instanceof WTrapError);
}
tests.defaultInitializedNullArrayRef = function()
{
let program = doPrep(`
int foo()
{
thread int[] p = null;
return p[0u];
}
`);
checkFail(
() => callFunction(program, "foo", [], []),
(e) => e instanceof WTrapError);
}
tests.defaultInitializedNullArrayRefIntLiteral = function()
{
let program = doPrep(`
int foo()
{
thread int[] p = null;
return p[0];
}
`);
checkFail(
() => callFunction(program, "foo", [], []),
(e) => e instanceof WTrapError);
}
tests.passNullToPtrMonomorphicArrayRef = function()
{
let program = doPrep(`
int foo(thread int[] ptr)
{
return ptr[0u];
}
int bar()
{
return foo(null);
}
`);
checkFail(
() => callFunction(program, "bar", [], []),
(e) => e instanceof WTrapError);
}
tests.passNullToPtrPolymorphicArrayRef = function()
{
checkFail(
() => doPrep(`
T foo<T>(thread T[] ptr)
{
return ptr[0u];
}
int bar()
{
return foo(null);
}
`),
(e) => e instanceof WTypeError);
}
tests.returnIntLiteralUint = function()
{
let program = doPrep("uint foo() { return 42; }");
checkNumber(program, callFunction(program, "foo", [], []), 42);
}
tests.returnIntLiteralDouble = function()
{
let program = doPrep("double foo() { return 42; }");
checkNumber(program, callFunction(program, "foo", [], []), 42);
}
tests.badIntLiteralForInt = function()
{
checkFail(
() => doPrep("void foo() { int x = 3000000000; }"),
(e) => e instanceof WSyntaxError);
}
tests.badIntLiteralForUint = function()
{
checkFail(
() => doPrep("void foo() { uint x = 5000000000; }"),
(e) => e instanceof WSyntaxError);
}
tests.badIntLiteralForDouble = function()
{
checkFail(
() => doPrep("void foo() { double x = 5000000000000000000000000000000000000; }"),
(e) => e instanceof WSyntaxError);
}
tests.passNullAndNotNull = function()
{
let program = doPrep(`
T bar<T>(device T* p, device T*)
{
return *p;
}
int foo(device int* p)
{
return bar(p, null);
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 13);
checkInt(program, callFunction(program, "foo", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 13);
}
tests.passNullAndNotNullFullPoly = function()
{
let program = doPrep(`
T bar<T>(T p, T)
{
return p;
}
int foo(device int* p)
{
return *bar(p, null);
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 13);
checkInt(program, callFunction(program, "foo", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 13);
}
tests.passNullAndNotNullFullPolyReverse = function()
{
let program = doPrep(`
T bar<T>(T, T p)
{
return p;
}
int foo(device int* p)
{
return *bar(null, p);
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 13);
checkInt(program, callFunction(program, "foo", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 13);
}
tests.nullTypeVariableUnify = function()
{
let left = new NullType(externalOrigin);
let right = new TypeVariable(externalOrigin, "T", null);
if (left.equals(right))
throw new Error("Should not be equal but are: " + left + " and " + right);
if (right.equals(left))
throw new Error("Should not be equal but are: " + left + " and " + right);
function everyOrder(array, callback)
{
function recurse(array, callback, order)
{
if (!array.length)
return callback.call(null, order);
for (let i = 0; i < array.length; ++i) {
let nextArray = array.concat();
nextArray.splice(i, 1);
recurse(nextArray, callback, order.concat([array[i]]));
}
}
recurse(array, callback, []);
}
function everyPair(things)
{
let result = [];
for (let i = 0; i < things.length; ++i) {
for (let j = 0; j < things.length; ++j) {
if (i != j)
result.push([things[i], things[j]]);
}
}
return result;
}
everyOrder(
everyPair(["nullType", "variableType", "ptrType"]),
order => {
let types = {};
types.nullType = new NullType(externalOrigin);
types.variableType = new TypeVariable(externalOrigin, "T", null);
types.ptrType = new PtrType(externalOrigin, "constant", new NativeType(externalOrigin, "foo_t", []));
let unificationContext = new UnificationContext([types.variableType]);
for (let [leftName, rightName] of order) {
let left = types[leftName];
let right = types[rightName];
let result = left.unify(unificationContext, right);
if (!result)
throw new Error("In order " + order + " cannot unify " + left + " with " + right);
}
if (!unificationContext.verify().result)
throw new Error("In order " + order.map(value => "(" + value + ")") + " cannot verify");
});
}
tests.doubleNot = function()
{
let program = doPrep(`
bool foo(bool x)
{
return !!x;
}
`);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, true)]), true);
checkBool(program, callFunction(program, "foo", [], [makeBool(program, false)]), false);
}
tests.simpleRecursion = function()
{
checkFail(
() => doPrep(`
void foo<T>(T x)
{
foo(&x);
}
`),
(e) => e instanceof WTypeError);
}
tests.protocolMonoSigPolyDef = function()
{
let program = doPrep(`
struct IntAnd<T> {
int first;
T second;
}
IntAnd<T> intAnd<T>(int first, T second)
{
IntAnd<T> result;
result.first = first;
result.second = second;
return result;
}
protocol IntAndable {
IntAnd<int> intAnd(IntAndable, int);
}
int foo<T:IntAndable>(T first, int second)
{
IntAnd<int> result = intAnd(first, second);
return result.first + result.second;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 54), makeInt(program, 12)]), 54 + 12);
}
tests.protocolPolySigPolyDef = function()
{
let program = doPrep(`
struct IntAnd<T> {
int first;
T second;
}
IntAnd<T> intAnd<T>(int first, T second)
{
IntAnd<T> result;
result.first = first;
result.second = second;
return result;
}
protocol IntAndable {
IntAnd<T> intAnd<T>(IntAndable, T);
}
int foo<T:IntAndable>(T first, int second)
{
IntAnd<int> result = intAnd(first, second);
return result.first + result.second;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 54), makeInt(program, 12)]), 54 + 12);
}
tests.protocolDoublePolySigDoublePolyDef = function()
{
let program = doPrep(`
struct IntAnd<T, U> {
int first;
T second;
U third;
}
IntAnd<T, U> intAnd<T, U>(int first, T second, U third)
{
IntAnd<T, U> result;
result.first = first;
result.second = second;
result.third = third;
return result;
}
protocol IntAndable {
IntAnd<T, U> intAnd<T, U>(IntAndable, T, U);
}
int foo<T:IntAndable>(T first, int second, int third)
{
IntAnd<int, int> result = intAnd(first, second, third);
return result.first + result.second + result.third;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 54), makeInt(program, 12), makeInt(program, 39)]), 54 + 12 + 39);
}
tests.protocolDoublePolySigDoublePolyDefExplicit = function()
{
let program = doPrep(`
struct IntAnd<T, U> {
int first;
T second;
U third;
}
IntAnd<T, U> intAnd<T, U>(int first, T second, U third)
{
IntAnd<T, U> result;
result.first = first;
result.second = second;
result.third = third;
return result;
}
protocol IntAndable {
IntAnd<T, U> intAnd<T, U>(IntAndable, T, U);
}
int foo<T:IntAndable>(T first, int second, int third)
{
IntAnd<int, int> result = intAnd<int, int>(first, second, third);
return result.first + result.second + result.third;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 54), makeInt(program, 12), makeInt(program, 39)]), 54 + 12 + 39);
}
tests.variableShadowing = function()
{
let program = doPrep(`
int foo()
{
int y;
int x = 7;
{
int x = 8;
y = x;
}
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 8);
program = doPrep(`
int foo()
{
int y;
int x = 7;
{
int x = 8;
}
y = x;
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 7);
}
tests.ifStatement = function()
{
let program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
y = 8;
}
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 8)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 6);
}
tests.ifElseStatement = function()
{
let program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
y = 8;
} else {
y = 9;
}
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 8)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 9);
}
tests.ifElseIfStatement = function()
{
let program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
y = 8;
} else if (x == 8) {
y = 9;
}
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 8)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 6);
}
tests.ifElseIfElseStatement = function()
{
let program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
y = 8;
} else if (x == 8) {
y = 9;
} else {
y = 10;
}
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 8)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 10);
}
tests.returnIf = function()
{
checkFail(
() => doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
return y;
}
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
return y;
} else {
y = 8;
}
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
y = 8;
} else {
return y;
}
}
`),
(e) => e instanceof WTypeError);
let program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
return 8;
} else {
return 10;
}
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 8)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 10);
checkFail(
() => doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
return 8;
} else if (x == 9) {
return 10;
}
}
`),
(e) => e instanceof WTypeError);
program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
return 8;
} else {
y = 9;
}
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 8)]), 9);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 9);
checkFail(
() => doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7) {
return 8;
} else {
return 10;
}
return 11;
}
`),
(e) => e instanceof WTypeError);
program = doPrep(`
int foo(int x)
{
int y = 6;
if (x == 7)
int y = 8;
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 6);
}
tests.simpleWhile = function()
{
let program = doPrep(`
int foo(int x)
{
while (x < 13)
x = x * 2;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 16);
}
tests.protocolMonoPolySigDoublePolyDefExplicit = function()
{
checkFail(
() => {
let program = doPrep(`
struct IntAnd<T, U> {
int first;
T second;
U third;
}
IntAnd<T, U> intAnd<T, U>(int first, T second, U third)
{
IntAnd<T, U> result;
result.first = first;
result.second = second;
result.third = third;
return result;
}
protocol IntAndable {
IntAnd<T, int> intAnd<T>(IntAndable, T, int);
}
int foo<T:IntAndable>(T first, int second, int third)
{
IntAnd<int, int> result = intAnd<int>(first, second, third);
return result.first + result.second + result.third;
}
`);
callFunction(program, "foo", [], [makeInt(program, 54), makeInt(program, 12), makeInt(program, 39)]);
},
(e) => e instanceof WTypeError);
}
tests.ambiguousOverloadSimple = function()
{
checkFail(
() => doPrep(`
void foo<T>(int, T) { }
void foo<T>(T, int) { }
void bar(int a, int b) { foo(a, b); }
`),
(e) => e instanceof WTypeError);
}
tests.ambiguousOverloadOverlapping = function()
{
checkFail(
() => doPrep(`
void foo<T>(int, T) { }
void foo<T>(T, T) { }
void bar(int a, int b) { foo(a, b); }
`),
(e) => e instanceof WTypeError);
}
tests.ambiguousOverloadTieBreak = function()
{
doPrep(`
void foo<T>(int, T) { }
void foo<T>(T, T) { }
void foo(int, int) { }
void bar(int a, int b) { foo(a, b); }
`);
}
tests.intOverloadResolution = function()
{
let program = doPrep(`
int foo(int) { return 1; }
int foo(uint) { return 2; }
int foo(double) { return 3; }
int bar() { return foo(42); }
`);
checkInt(program, callFunction(program, "bar", [], []), 1);
}
tests.intOverloadResolutionReverseOrder = function()
{
let program = doPrep(`
int foo(double) { return 3; }
int foo(uint) { return 2; }
int foo(int) { return 1; }
int bar() { return foo(42); }
`);
checkInt(program, callFunction(program, "bar", [], []), 1);
}
tests.intOverloadResolutionGeneric = function()
{
let program = doPrep(`
int foo(int) { return 1; }
int foo<T>(T) { return 2; }
int bar() { return foo(42); }
`);
checkInt(program, callFunction(program, "bar", [], []), 1);
}
tests.intLiteralGeneric = function()
{
let program = doPrep(`
int foo<T>(T x) { return 3478; }
int bar() { return foo(42); }
`);
checkInt(program, callFunction(program, "bar", [], []), 3478);
}
tests.intLiteralGenericWithProtocols = function()
{
let program = doPrep(`
protocol MyConvertibleToInt {
operator int(MyConvertibleToInt);
}
int foo<T:MyConvertibleToInt>(T x) { return int(x); }
int bar() { return foo(42); }
`);
checkInt(program, callFunction(program, "bar", [], []), 42);
}
tests.uintLiteralGeneric = function()
{
let program = doPrep(`
int foo<T>(T x) { return 3478; }
int bar() { return foo(42u); }
`);
checkInt(program, callFunction(program, "bar", [], []), 3478);
}
tests.uintLiteralGenericWithProtocols = function()
{
let program = doPrep(`
protocol MyConvertibleToUint {
operator uint(MyConvertibleToUint);
}
uint foo<T:MyConvertibleToUint>(T x) { return uint(x); }
uint bar() { return foo(42u); }
`);
checkUint(program, callFunction(program, "bar", [], []), 42);
}
tests.intLiteralGenericSpecific = function()
{
let program = doPrep(`
T foo<T>(T x) { return x; }
int bar() { return foo(int(42)); }
`);
checkInt(program, callFunction(program, "bar", [], []), 42);
}
tests.simpleConstexpr = function()
{
let program = doPrep(`
int foo<int a>(int b)
{
return a + b;
}
int bar(int b)
{
return foo<42>(b);
}
`);
checkInt(program, callFunction(program, "bar", [], [makeInt(program, 58)]), 58 + 42);
}
tests.break = function()
{
let program = doPrep(`
int foo(int x)
{
while (true) {
x = x * 2;
if (x >= 7)
break;
}
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 10)]), 20);
program = doPrep(`
int foo(int x)
{
while (true) {
while (true) {
x = x * 2;
if (x >= 7)
break;
}
x = x - 1;
break;
}
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 10)]), 19);
checkFail(
() => doPrep(`
int foo(int x)
{
while (true) {
{
break;
}
x = x + 1;
}
return x;
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
int foo(int x)
{
break;
return x;
}
`),
(e) => e instanceof WTypeError);
program = doPrep(`
int foo(int x)
{
while (true) {
if (x == 7) {
break;
}
x = x + 1;
}
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 7);
program = doPrep(`
int foo(int x)
{
while (true) {
break;
}
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 1);
program = doPrep(`
int foo()
{
while (true) {
return 7;
}
}
`);
checkInt(program, callFunction(program, "foo", [], []), 7);
checkFail(
() => doPrep(`
int foo(int x)
{
while(true) {
break;
return 7;
}
}
`),
(e) => e instanceof WTypeError);
}
tests.continue = function()
{
let program = doPrep(`
int foo(int x)
{
while (x < 10) {
if (x == 8) {
x = x + 1;
continue;
}
x = x * 2;
}
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 18);
checkFail(
() => doPrep(`
int foo(int x)
{
continue;
return x;
}
`),
(e) => e instanceof WTypeError);
}
tests.doWhile = function()
{
let program = doPrep(`
int foo(int x)
{
int y = 7;
do {
y = 8;
break;
} while (x < 10);
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 8);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 11)]), 8);
program = doPrep(`
int foo(int x)
{
int y = 7;
do {
y = 8;
break;
} while (y == 7);
return y;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 1)]), 8);
program = doPrep(`
int foo(int x)
{
int sum = 0;
do {
if (x == 11) {
x = 15;
continue;
}
sum = sum + x;
x = x + 1;
} while (x < 13);
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 9)]), 19);
}
tests.forLoop = function()
{
let program = doPrep(`
int foo(int x)
{
int sum = 0;
int i;
for (i = 0; i < x; i = i + 1) {
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
program = doPrep(`
int foo(int x)
{
int sum = 0;
for (int i = 0; i < x; i = i + 1) {
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
program = doPrep(`
int foo(int x)
{
int sum = 0;
int i = 100;
for (int i = 0; i < x; i = i + 1) {
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
program = doPrep(`
int foo(int x)
{
int sum = 0;
for (int i = 0; i < x; i = i + 1) {
if (i == 4)
continue;
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 11);
program = doPrep(`
int foo(int x)
{
int sum = 0;
for (int i = 0; i < x; i = i + 1) {
if (i == 5)
break;
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 10);
program = doPrep(`
int foo(int x)
{
int sum = 0;
for (int i = 0; ; i = i + 1) {
if (i >= x)
break;
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 15);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 21);
program = doPrep(`
int foo(int x)
{
int sum = 0;
int i = 0;
for ( ; ; i = i + 1) {
if (i >= x)
break;
sum = sum + i;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 15);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 21);
program = doPrep(`
int foo(int x)
{
int sum = 0;
int i = 0;
for ( ; ; ) {
if (i >= x)
break;
sum = sum + i;
i = i + 1;
}
return sum;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 3);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 6);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 10);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 15);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 21);
checkFail(
() => doPrep(`
void foo(int x)
{
for (int i = 0; ; i = i + 1) {
break;
x = i;
}
}
`),
(e) => e instanceof WTypeError);
program = doPrep(`
int foo(int x)
{
for ( ; ; ) {
return 7;
}
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 7);
checkFail(
() => doPrep(`
int foo(int x)
{
for ( ; x < 10; ) {
return 7;
}
}
`),
(e) => e instanceof WTypeError);
program = doPrep(`
int foo(int x)
{
for ( ; true; ) {
return 7;
}
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 3)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 4)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 5)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 6)]), 7);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 7)]), 7);
}
tests.chainConstexpr = function()
{
let program = doPrep(`
int foo<int a>(int b)
{
return a + b;
}
int bar<int a>(int b)
{
return foo<a>(b);
}
int baz(int b)
{
return bar<42>(b);
}
`);
checkInt(program, callFunction(program, "baz", [], [makeInt(program, 58)]), 58 + 42);
}
tests.chainGeneric = function()
{
let program = doPrep(`
T foo<T>(T x)
{
return x;
}
T bar<T>(thread T* ptr)
{
return *foo(ptr);
}
int baz(int x)
{
return bar(&x);
}
`);
checkInt(program, callFunction(program, "baz", [], [makeInt(program, 37)]), 37);
}
tests.chainStruct = function()
{
let program = doPrep(`
struct Foo<T> {
T f;
}
struct Bar<T> {
Foo<thread T*> f;
}
int foo(thread Bar<int>* x)
{
return *x->f.f;
}
int bar(int a)
{
Bar<int> x;
x.f.f = &a;
return foo(&x);
}
`);
checkInt(program, callFunction(program, "bar", [], [makeInt(program, 4657)]), 4657);
}
tests.chainStructNewlyValid = function()
{
let program = doPrep(`
struct Foo<T> {
T f;
}
struct Bar<T> {
Foo<device T*> f;
}
int foo(thread Bar<int>* x)
{
return *x->f.f;
}
int bar(device int* a)
{
Bar<int> x;
x.f.f = a;
return foo(&x);
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 78453);
checkInt(program, callFunction(program, "bar", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 78453);
}
tests.chainStructDevice = function()
{
let program = doPrep(`
struct Foo<T> {
T f;
}
struct Bar<T> {
Foo<device T*> f;
}
int foo(thread Bar<int>* x)
{
return *x->f.f;
}
int bar(device int* a)
{
Bar<int> x;
x.f.f = a;
return foo(&x);
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 79201);
checkInt(program, callFunction(program, "bar", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 79201);
}
tests.paramChainStructDevice = function()
{
let program = doPrep(`
struct Foo<T> {
T f;
}
struct Bar<T> {
Foo<T> f;
}
int foo(thread Bar<device int*>* x)
{
return *x->f.f;
}
int bar(device int* a)
{
Bar<device int*> x;
x.f.f = a;
return foo(&x);
}
`);
let buffer = new EBuffer(1);
buffer.set(0, 79201);
checkInt(program, callFunction(program, "bar", [], [TypedValue.box(new PtrType(externalOrigin, "device", program.intrinsics.int32), new EPtr(buffer, 0))]), 79201);
}
tests.simpleProtocolExtends = function()
{
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);
}
tests.protocolExtendsTwo = function()
{
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);
}
tests.prefixPlusPlus = function()
{
let program = doPrep(`
int foo(int x)
{
++x;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
}
tests.prefixPlusPlusResult = function()
{
let program = doPrep(`
int foo(int x)
{
return ++x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
}
tests.postfixPlusPlus = function()
{
let program = doPrep(`
int foo(int x)
{
x++;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
}
tests.postfixPlusPlusResult = function()
{
let program = doPrep(`
int foo(int x)
{
return x++;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 64);
}
tests.prefixMinusMinus = function()
{
let program = doPrep(`
int foo(int x)
{
--x;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
}
tests.prefixMinusMinusResult = function()
{
let program = doPrep(`
int foo(int x)
{
return --x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
}
tests.postfixMinusMinus = function()
{
let program = doPrep(`
int foo(int x)
{
x--;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
}
tests.postfixMinusMinusResult = function()
{
let program = doPrep(`
int foo(int x)
{
return x--;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 64);
}
tests.plusEquals = function()
{
let program = doPrep(`
int foo(int x)
{
x += 42;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 + 42);
}
tests.plusEqualsResult = function()
{
let program = doPrep(`
int foo(int x)
{
return x += 42;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 + 42);
}
tests.minusEquals = function()
{
let program = doPrep(`
int foo(int x)
{
x -= 42;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 - 42);
}
tests.minusEqualsResult = function()
{
let program = doPrep(`
int foo(int x)
{
return x -= 42;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 - 42);
}
tests.timesEquals = function()
{
let program = doPrep(`
int foo(int x)
{
x *= 42;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 * 42);
}
tests.timesEqualsResult = function()
{
let program = doPrep(`
int foo(int x)
{
return x *= 42;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 * 42);
}
tests.divideEquals = function()
{
let program = doPrep(`
int foo(int x)
{
x /= 42;
return x;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), (385 / 42) | 0);
}
tests.divideEqualsResult = function()
{
let program = doPrep(`
int foo(int x)
{
return x /= 42;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), (385 / 42) | 0);
}
tests.twoIntLiterals = function()
{
let program = doPrep(`
bool foo()
{
return 42 == 42;
}
`);
checkBool(program, callFunction(program, "foo", [], []), true);
}
tests.unifyDifferentLiterals = function()
{
checkFail(
() => doPrep(`
void bar<T>(T, T)
{
}
void foo()
{
bar(42, 42u);
}
`),
(e) => e instanceof WTypeError);
}
tests.unifyDifferentLiteralsBackwards = function()
{
checkFail(
() => doPrep(`
void bar<T>(T, T)
{
}
void foo()
{
bar(42u, 42);
}
`),
(e) => e instanceof WTypeError);
}
tests.unifyVeryDifferentLiterals = function()
{
checkFail(
() => doPrep(`
void bar<T>(T, T)
{
}
void foo()
{
bar(42, null);
}
`),
(e) => e instanceof WTypeError);
}
tests.unifyVeryDifferentLiteralsBackwards = function()
{
checkFail(
() => doPrep(`
void bar<T>(T, T)
{
}
void foo()
{
bar(null, 42);
}
`),
(e) => e instanceof WTypeError);
}
tests.assignUintToInt = function()
{
checkFail(
() => doPrep(`
void foo()
{
int x = 42u;
}
`),
(e) => e instanceof WTypeError && e.message.indexOf("Type mismatch in variable initialization") != -1);
}
tests.buildArrayThenSumIt = function()
{
let program = doPrep(`
int foo()
{
int[42] array;
for (uint i = 0; i < 42; i = i + 1)
array[i] = int(i + 5);
int result;
for (uint i = 0; i < 42; i = i + 1)
result = result + array[i];
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 42 * 5 + 42 * 41 / 2);
}
tests.buildArrayThenSumItUsingArrayReference = function()
{
let program = doPrep(`
int bar(thread int[] array)
{
for (uint i = 0; i < 42; i = i + 1)
array[i] = int(i + 5);
int result;
for (uint i = 0; i < 42; i = i + 1)
result = result + array[i];
return result;
}
int foo()
{
int[42] array;
return bar(@array);
}
`);
checkInt(program, callFunction(program, "foo", [], []), 42 * 5 + 42 * 41 / 2);
}
tests.overrideSubscriptStruct = function()
{
let program = doPrep(`
struct Foo {
int x;
int y;
}
thread int* operator&[](thread Foo* foo, uint index)
{
if (index == 0)
return &foo->x;
if (index == 1)
return &foo->y;
return null;
}
int foo()
{
Foo foo;
foo.x = 498;
foo.y = 19;
return foo[0] + foo[1] * 3;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 498 + 19 * 3);
}
tests.overrideSubscriptStructAndDoStores = function()
{
let program = doPrep(`
struct Foo {
int x;
int y;
}
thread int* operator&[](thread Foo* foo, uint index)
{
if (index == 0)
return &foo->x;
if (index == 1)
return &foo->y;
return null;
}
int foo()
{
Foo foo;
foo[0] = 498;
foo[1] = 19;
return foo.x + foo.y;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 498 + 19);
}
tests.overrideSubscriptStructAndUsePointers = function()
{
let program = doPrep(`
struct Foo {
int x;
int y;
}
thread int* operator&[](thread Foo* foo, uint index)
{
if (index == 0)
return &foo->x;
if (index == 1)
return &foo->y;
return null;
}
int bar(thread Foo* foo)
{
return (*foo)[0] + (*foo)[1];
}
int foo()
{
Foo foo;
foo.x = 498;
foo.y = 19;
return bar(&foo);
}
`);
checkInt(program, callFunction(program, "foo", [], []), 498 + 19);
}
tests.overrideSubscriptStructAndUsePointersIncorrectly = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
int y;
}
thread int* operator&[](thread Foo* foo, uint index)
{
if (index == 0)
return &foo->x;
if (index == 1)
return &foo->y;
return null;
}
int bar(thread Foo* foo)
{
return foo[0] + foo[1];
}
int foo()
{
Foo foo;
foo.x = 498;
foo.y = 19;
return bar(&foo);
}
`),
(e) => e instanceof WTypeError);
}
tests.makeArrayRefFromLocal = function()
{
let program = doPrep(`
int bar(thread int[] ref)
{
return ref[0];
}
int foo()
{
int x = 48;
return bar(@x);
}
`);
checkInt(program, callFunction(program, "foo", [], []), 48);
}
tests.makeArrayRefFromPointer = function()
{
let program = doPrep(`
int bar(thread int[] ref)
{
return ref[0];
}
int baz(thread int* ptr)
{
return bar(@ptr);
}
int foo()
{
int x = 48;
return baz(&x);
}
`);
checkInt(program, callFunction(program, "foo", [], []), 48);
}
tests.makeArrayRefFromArrayRef = function()
{
checkFail(
() => doPrep(`
int bar(thread int[] ref)
{
return ref[0];
}
int baz(thread int[] ptr)
{
return bar(@ptr);
}
int foo()
{
int x = 48;
return baz(@x);
}
`),
(e) => e instanceof WTypeError);
}
tests.simpleLength = function()
{
let program = doPrep(`
uint foo()
{
double[754] array;
return (@array).length;
}
`);
checkUint(program, callFunction(program, "foo", [], []), 754);
}
tests.nonArrayRefArrayLengthSucceed = function()
{
let program = doPrep(`
uint foo()
{
double[754] array;
return array.length;
}
`);
checkUint(program, callFunction(program, "foo", [], []), 754);
}
tests.nonArrayRefArrayLengthFail = function()
{
checkFail(
() => doPrep(`
thread uint* lengthPtr()
{
int[42] array;
return &(array.length);
}
`),
e => e instanceof WTypeError);
}
tests.constexprIsNotLValuePtr = function()
{
checkFail(
() => doPrep(`
thread int* foo<int x>()
{
return &x;
}
`),
e => e instanceof WTypeError);
}
tests.constexprIsNotLValueAssign = function()
{
checkFail(
() => doPrep(`
void foo<int x>()
{
x = 42;
}
`),
e => e instanceof WTypeError);
}
tests.constexprIsNotLValueRMW = function()
{
checkFail(
() => doPrep(`
void foo<int x>()
{
x += 42;
}
`),
e => e instanceof WTypeError);
}
tests.assignLength = function()
{
checkFail(
() => doPrep(`
void foo()
{
double[754] array;
(@array).length = 42;
}
`),
(e) => e instanceof WTypeError && e.message.indexOf("Have neither ander nor setter") != -1);
}
tests.assignLengthHelper = function()
{
checkFail(
() => doPrep(`
void bar(thread double[] array)
{
array.length = 42;
}
void foo()
{
double[754] array;
bar(@array);
}
`),
(e) => e instanceof WTypeError && e.message.indexOf("Have neither ander nor setter") != -1);
}
tests.simpleGetter = function()
{
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);
}
tests.simpleSetter = function()
{
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);
}
tests.genericAccessors = function()
{
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);
}
tests.bitSubscriptAccessor = function()
{
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);
}
tests.nestedSubscriptLValueEmulationSimple = function()
{
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);
}
tests.nestedSubscriptLValueEmulationGeneric = function()
{
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);
}
tests.boolBitAnd = function()
{
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);
}
tests.boolBitOr = function()
{
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);
}
tests.boolBitXor = function()
{
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);
}
tests.boolBitNot = function()
{
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);
}
tests.intBitAnd = function()
{
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);
}
tests.intBitOr = function()
{
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);
}
tests.intBitXor = function()
{
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);
}
tests.intBitNot = function()
{
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);
}
tests.intLShift = function()
{
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);
}
tests.intRShift = function()
{
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);
}
tests.uintBitAnd = function()
{
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);
}
tests.uintBitOr = function()
{
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);
}
tests.uintBitXor = function()
{
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);
}
tests.uintBitNot = function()
{
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);
}
tests.uintLShift = function()
{
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);
}
tests.uintRShift = function()
{
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);
}
tests.uint8BitAnd = function()
{
let program = doPrep(`
uint8 foo(uint8 a, uint8 b)
{
return a & b;
}
`);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint8(program, 7)]), 1);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint8(program, 42)]), 42);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint8(program, -7)]), 249);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint8(program, 85732)]), 0);
}
tests.uint8BitOr = function()
{
let program = doPrep(`
uint8 foo(uint8 a, uint8 b)
{
return a | b;
}
`);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint8(program, 7)]), 7);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint8(program, 42)]), 255);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint8(program, -7)]), 255);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint8(program, 85732)]), 228);
}
tests.uint8BitXor = function()
{
let program = doPrep(`
uint8 foo(uint8 a, uint8 b)
{
return a ^ b;
}
`);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint8(program, 7)]), 6);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint8(program, 42)]), 213);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint8(program, -7)]), 6);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint8(program, 85732)]), 228);
}
tests.uint8BitNot = function()
{
let program = doPrep(`
uint8 foo(uint8 a)
{
return ~a;
}
`);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1)]), 254);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535)]), 0);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1)]), 0);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0)]), 255);
}
tests.uint8LShift = function()
{
let program = doPrep(`
uint8 foo(uint8 a, uint b)
{
return a << b;
}
`);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint(program, 7)]), 128);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint(program, 2)]), 252);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint(program, 5)]), 224);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint(program, 3)]), 0);
}
tests.uint8RShift = function()
{
let program = doPrep(`
uint8 foo(uint8 a, uint b)
{
return a >> b;
}
`);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint(program, 7)]), 0);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint(program, 2)]), 255);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint(program, 5)]), 255);
checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint(program, 3)]), 0);
}
tests.floatMath = function()
{
let program = doPrep(`
bool foo()
{
return 42.5 == 42.5;
}
bool foo2()
{
return 42.5f == 42.5;
}
bool foo3()
{
return 42.5 == 42.5f;
}
bool foo4()
{
return 42.5f == 42.5f;
}
bool foo5()
{
return 42.5d == 42.5d;
}
float bar(float x)
{
return x;
}
float foo6()
{
return bar(7.5);
}
float foo7()
{
return bar(7.5f);
}
float foo8()
{
return bar(7.5d);
}
float foo9()
{
return float(7.5);
}
float foo10()
{
return float(7.5f);
}
float foo11()
{
return float(7.5d);
}
float foo12()
{
return float(7);
}
float foo13()
{
double x = 7.5d;
return float(x);
}
double foo14()
{
double x = 7.5f;
return double(x);
}
`);
checkBool(program, callFunction(program, "foo", [], []), true);
checkBool(program, callFunction(program, "foo2", [], []), true);
checkBool(program, callFunction(program, "foo3", [], []), true);
checkBool(program, callFunction(program, "foo4", [], []), true);
checkBool(program, callFunction(program, "foo5", [], []), true);
checkFloat(program, callFunction(program, "foo6", [], []), 7.5);
checkFloat(program, callFunction(program, "foo7", [], []), 7.5);
checkFloat(program, callFunction(program, "foo8", [], []), 7.5);
checkFloat(program, callFunction(program, "foo9", [], []), 7.5);
checkFloat(program, callFunction(program, "foo10", [], []), 7.5);
checkFloat(program, callFunction(program, "foo11", [], []), 7.5);
checkFloat(program, callFunction(program, "foo12", [], []), 7);
checkFloat(program, callFunction(program, "foo13", [], []), 7.5);
checkDouble(program, callFunction(program, "foo14", [], []), 7.5);
checkFail(
() => doPrep(`
int bar(int x)
{
return x;
}
int foo()
{
bar(4.);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
int bar(int x)
{
return x;
}
int foo()
{
bar(4.d);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
int bar(int x)
{
return x;
}
int foo()
{
bar(4.f);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
uint bar(uint x)
{
return x;
}
int foo()
{
bar(4.);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
uint bar(uint x)
{
return x;
}
int foo()
{
bar(4.d);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
uint bar(uint x)
{
return x;
}
int foo()
{
bar(4.f);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
float bar(float x)
{
return x;
}
void foo()
{
bar(16777217.d);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
float bar(float x)
{
return x;
}
float foo()
{
double x = 7.;
return bar(x);
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
float foo()
{
double x = 7.;
return x;
}
`),
(e) => e instanceof WTypeError);
}
tests.genericCastInfer = function()
{
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);
}
tests.booleanMath = function()
{
let program = doPrep(`
bool foo()
{
return true && true;
}
bool foo2()
{
return true && false;
}
bool foo3()
{
return false && true;
}
bool foo4()
{
return false && false;
}
bool foo5()
{
return true || true;
}
bool foo6()
{
return true || false;
}
bool foo7()
{
return false || true;
}
bool foo8()
{
return false || false;
}
`);
checkBool(program, callFunction(program, "foo", [], []), true);
checkBool(program, callFunction(program, "foo2", [], []), false);
checkBool(program, callFunction(program, "foo3", [], []), false);
checkBool(program, callFunction(program, "foo4", [], []), false);
checkBool(program, callFunction(program, "foo5", [], []), true);
checkBool(program, callFunction(program, "foo6", [], []), true);
checkBool(program, callFunction(program, "foo7", [], []), true);
checkBool(program, callFunction(program, "foo8", [], []), false);
}
tests.typedefArray = function()
{
let program = doPrep(`
typedef ArrayTypedef = int[2];
int foo()
{
ArrayTypedef arrayTypedef;
return arrayTypedef[0];
}
`);
checkInt(program, callFunction(program, "foo", [], []), 0);
}
tests.shaderTypes = function()
{
checkFail(
() => doPrep(`
struct Foo {
float4 x;
}
vertex Foo bar()
{
Foo result;
result.x = float4();
return result;
}
Foo foo() {
return bar();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
vertex float bar()
{
return 4.;
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
float4 x;
}
vertex Foo bar(device Foo* x)
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Boo {
float4 x;
}
struct Foo {
float4 x;
device Boo* y;
}
vertex Foo bar()
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
float4 x;
}
struct Boo {
device Foo* y;
}
vertex Foo bar(Boo b)
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
float4 x;
}
vertex Foo bar(device Foo* x)
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
float4 x;
}
fragment Foo bar(Foo foo)
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
float4 x;
}
fragment Foo bar(device Foo* stageIn)
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Boo {
float4 x;
}
struct Foo {
float4 x;
device Boo* y;
}
fragment Boo bar(Foo stageIn)
{
return boo();
}
`),
(e) => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Boo {
float4 x;
}
struct Foo {
float4 x;
device Boo* y;
}
fragment Foo bar(Boo stageIn)
{
return Foo();
}
`),
(e) => e instanceof WTypeError);
}
tests.builtinVectors = function()
{
let program = doPrep(`
int foo()
{
int2 a = int2(3, 4);
return a[0];
}
int foo2()
{
int2 a = int2(3, 4);
int3 b = int3(a, 5);
return b[1];
}
int foo3()
{
int3 a = int3(3, 4, 5);
int4 b = int4(6, a);
return b[1];
}
int foo4()
{
int2 a = int2(3, 4);
int2 b = int2(5, 6);
int4 c = int4(a, b);
return c[2];
}
bool foo5()
{
int4 a = int4(3, 4, 5, 6);
int2 b = int2(4, 5);
int4 c = int4(3, b, 6);
return a == c;
}
bool foo6()
{
int2 a = int2(4, 5);
int3 b = int3(3, a);
int3 c = int3(3, 4, 6);
return b == c;
}
uint foou()
{
uint2 a = uint2(3, 4);
return a[0];
}
uint foou2()
{
uint2 a = uint2(3, 4);
uint3 b = uint3(a, 5);
return b[1];
}
uint foou3()
{
uint3 a = uint3(3, 4, 5);
uint4 b = uint4(6, a);
return b[1];
}
uint foou4()
{
uint2 a = uint2(3, 4);
uint2 b = uint2(5, 6);
uint4 c = uint4(a, b);
return c[2];
}
bool foou5()
{
uint4 a = uint4(3, 4, 5, 6);
uint2 b = uint2(4, 5);
uint4 c = uint4(3, b, 6);
return a == c;
}
bool foou6()
{
uint2 a = uint2(4, 5);
uint3 b = uint3(3, a);
uint3 c = uint3(3, 4, 6);
return b == c;
}
float foof()
{
float2 a = float2(3., 4.);
return a[0];
}
float foof2()
{
float2 a = float2(3., 4.);
float3 b = float3(a, 5.);
return b[1];
}
float foof3()
{
float3 a = float3(3., 4., 5.);
float4 b = float4(6., a);
return b[1];
}
float foof4()
{
float2 a = float2(3., 4.);
float2 b = float2(5., 6.);
float4 c = float4(a, b);
return c[2];
}
bool foof5()
{
float4 a = float4(3., 4., 5., 6.);
float2 b = float2(4., 5.);
float4 c = float4(3., b, 6.);
return a == c;
}
bool foof6()
{
float2 a = float2(4., 5.);
float3 b = float3(3., a);
float3 c = float3(3., 4., 6.);
return b == c;
}
double food()
{
double2 a = double2(3., 4.);
return a[0];
}
double food2()
{
double2 a = double2(3., 4.);
double3 b = double3(a, 5.);
return b[1];
}
double food3()
{
double3 a = double3(3., 4., 5.);
double4 b = double4(6., a);
return b[1];
}
double food4()
{
double2 a = double2(3., 4.);
double2 b = double2(5., 6.);
double4 c = double4(a, b);
return c[2];
}
bool food5()
{
double4 a = double4(3., 4., 5., 6.);
double2 b = double2(4., 5.);
double4 c = double4(3., b, 6.);
return a == c;
}
bool food6()
{
double2 a = double2(4., 5.);
double3 b = double3(3., a);
double3 c = double3(3., 4., 6.);
return b == c;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 3);
checkInt(program, callFunction(program, "foo2", [], []), 4);
checkInt(program, callFunction(program, "foo3", [], []), 3);
checkInt(program, callFunction(program, "foo4", [], []), 5);
checkBool(program, callFunction(program, "foo5", [], []), true);
checkBool(program, callFunction(program, "foo6", [], []), false);
checkUint(program, callFunction(program, "foou", [], []), 3);
checkUint(program, callFunction(program, "foou2", [], []), 4);
checkUint(program, callFunction(program, "foou3", [], []), 3);
checkUint(program, callFunction(program, "foou4", [], []), 5);
checkBool(program, callFunction(program, "foou5", [], []), true);
checkBool(program, callFunction(program, "foou6", [], []), false);
checkFloat(program, callFunction(program, "foof", [], []), 3);
checkFloat(program, callFunction(program, "foof2", [], []), 4);
checkFloat(program, callFunction(program, "foof3", [], []), 3);
checkFloat(program, callFunction(program, "foof4", [], []), 5);
checkBool(program, callFunction(program, "foof5", [], []), true);
checkBool(program, callFunction(program, "foof6", [], []), false);
checkDouble(program, callFunction(program, "food", [], []), 3);
checkDouble(program, callFunction(program, "food2", [], []), 4);
checkDouble(program, callFunction(program, "food3", [], []), 3);
checkDouble(program, callFunction(program, "food4", [], []), 5);
checkBool(program, callFunction(program, "food5", [], []), true);
checkBool(program, callFunction(program, "food6", [], []), false);
}
tests.instantiateStructInStruct = function()
{
let program = doPrep(`
struct Bar<T> {
T x;
}
struct Foo {
Bar<int> x;
}
int foo()
{
Foo x;
x.x.x = 42;
x.x.x++;
return x.x.x;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 43);
}
tests.instantiateStructInStructWithInt2 = function()
{
let program = doPrep(`
struct Foo {
int2 x;
}
int foo()
{
Foo x;
x.x.x = 42;
x.x.x++;
return x.x.x;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 43);
}
tests.simpleEnum = function()
{
let program = doPrep(`
enum Foo {
War,
Famine,
Pestilence,
Death
}
Foo war()
{
return Foo.War;
}
Foo famine()
{
return Foo.Famine;
}
Foo pestilence()
{
return Foo.Pestilence;
}
Foo death()
{
return Foo.Death;
}
bool equals(Foo a, Foo b)
{
return a == b;
}
bool notEquals(Foo a, Foo b)
{
return a != b;
}
bool testSimpleEqual()
{
return equals(Foo.War, Foo.War);
}
bool testAnotherEqual()
{
return equals(Foo.Pestilence, Foo.Pestilence);
}
bool testNotEqual()
{
return equals(Foo.Famine, Foo.Death);
}
bool testSimpleNotEqual()
{
return notEquals(Foo.War, Foo.War);
}
bool testAnotherNotEqual()
{
return notEquals(Foo.Pestilence, Foo.Pestilence);
}
bool testNotNotEqual()
{
return notEquals(Foo.Famine, Foo.Death);
}
int intWar()
{
return int(war());
}
int intFamine()
{
return int(famine());
}
int intPestilence()
{
return int(pestilence());
}
int intDeath()
{
return int(death());
}
int warValue()
{
return war().value;
}
int famineValue()
{
return famine().value;
}
int pestilenceValue()
{
return pestilence().value;
}
int deathValue()
{
return death().value;
}
int warValueLiteral()
{
return Foo.War.value;
}
int famineValueLiteral()
{
return Foo.Famine.value;
}
int pestilenceValueLiteral()
{
return Foo.Pestilence.value;
}
int deathValueLiteral()
{
return Foo.Death.value;
}
Foo intWarBackwards()
{
return Foo(intWar());
}
Foo intFamineBackwards()
{
return Foo(intFamine());
}
Foo intPestilenceBackwards()
{
return Foo(intPestilence());
}
Foo intDeathBackwards()
{
return Foo(intDeath());
}
`);
checkEnum(program, callFunction(program, "war", [], []), 0);
checkEnum(program, callFunction(program, "famine", [], []), 1);
checkEnum(program, callFunction(program, "pestilence", [], []), 2);
checkEnum(program, callFunction(program, "death", [], []), 3);
checkBool(program, callFunction(program, "testSimpleEqual", [], []), true);
checkBool(program, callFunction(program, "testAnotherEqual", [], []), true);
checkBool(program, callFunction(program, "testNotEqual", [], []), false);
checkBool(program, callFunction(program, "testSimpleNotEqual", [], []), false);
checkBool(program, callFunction(program, "testAnotherNotEqual", [], []), false);
checkBool(program, callFunction(program, "testNotNotEqual", [], []), true);
checkInt(program, callFunction(program, "intWar", [], []), 0);
checkInt(program, callFunction(program, "intFamine", [], []), 1);
checkInt(program, callFunction(program, "intPestilence", [], []), 2);
checkInt(program, callFunction(program, "intDeath", [], []), 3);
checkInt(program, callFunction(program, "warValue", [], []), 0);
checkInt(program, callFunction(program, "famineValue", [], []), 1);
checkInt(program, callFunction(program, "pestilenceValue", [], []), 2);
checkInt(program, callFunction(program, "deathValue", [], []), 3);
checkInt(program, callFunction(program, "warValueLiteral", [], []), 0);
checkInt(program, callFunction(program, "famineValueLiteral", [], []), 1);
checkInt(program, callFunction(program, "pestilenceValueLiteral", [], []), 2);
checkInt(program, callFunction(program, "deathValueLiteral", [], []), 3);
checkEnum(program, callFunction(program, "intWarBackwards", [], []), 0);
checkEnum(program, callFunction(program, "intFamineBackwards", [], []), 1);
checkEnum(program, callFunction(program, "intPestilenceBackwards", [], []), 2);
checkEnum(program, callFunction(program, "intDeathBackwards", [], []), 3);
}
tests.enumWithManualValues = function()
{
let program = doPrep(`
enum Foo {
War = 72,
Famine = 0,
Pestilence = 23,
Death = -42
}
Foo war()
{
return Foo.War;
}
Foo famine()
{
return Foo.Famine;
}
Foo pestilence()
{
return Foo.Pestilence;
}
Foo death()
{
return Foo.Death;
}
`);
checkEnum(program, callFunction(program, "war", [], []), 72);
checkEnum(program, callFunction(program, "famine", [], []), 0);
checkEnum(program, callFunction(program, "pestilence", [], []), 23);
checkEnum(program, callFunction(program, "death", [], []), -42);
}
tests.enumWithoutZero = function()
{
checkFail(
() => doPrep(`
enum Foo {
War = 72,
Famine = 64,
Pestilence = 23,
Death = -42
}
`),
e => e instanceof WTypeError);
}
tests.enumDuplicates = function()
{
checkFail(
() => doPrep(`
enum Foo {
War = -42,
Famine = 0,
Pestilence = 23,
Death = -42
}
`),
e => e instanceof WTypeError);
}
tests.enumWithSomeManualValues = function()
{
let program = doPrep(`
enum Foo {
War = 72,
Famine,
Pestilence = 0,
Death
}
Foo war()
{
return Foo.War;
}
Foo famine()
{
return Foo.Famine;
}
Foo pestilence()
{
return Foo.Pestilence;
}
Foo death()
{
return Foo.Death;
}
`);
checkEnum(program, callFunction(program, "war", [], []), 72);
checkEnum(program, callFunction(program, "famine", [], []), 73);
checkEnum(program, callFunction(program, "pestilence", [], []), 0);
checkEnum(program, callFunction(program, "death", [], []), 1);
}
tests.enumConstexprGenericFunction = function()
{
let program = doPrep(`
enum Axis { X, Y }
int foo<Axis axis>() { return int(axis); }
int testX() { return foo<Axis.X>(); }
int testY() { return foo<Axis.Y>(); }
`);
checkInt(program, callFunction(program, "testX", [], []), 0);
checkInt(program, callFunction(program, "testY", [], []), 1);
}
tests.enumConstexprGenericStruct = function()
{
let program = doPrep(`
enum Axis { X, Y }
struct Foo<Axis axis> { }
int foo<Axis axis>(Foo<axis>) { return int(axis); }
int testX()
{
Foo<Axis.X> f;
return foo(f);
}
int testY()
{
Foo<Axis.Y> f;
return foo(f);
}
`);
checkInt(program, callFunction(program, "testX", [], []), 0);
checkInt(program, callFunction(program, "testY", [], []), 1);
}
tests.trap = function()
{
let program = doPrep(`
int foo()
{
trap;
}
int foo2(int x)
{
if (x == 3)
trap;
return 4;
}
struct Bar {
int3 x;
float y;
}
Bar foo3()
{
trap;
}
`);
checkFail(
() => callFunction(program, "foo", [], []),
e => e instanceof WTrapError);
checkInt(program, callFunction(program, "foo2", [], [makeInt(program, 1)]), 4);
checkFail(
() => callFunction(program, "foo2", [], [makeInt(program, 3)]),
e => e instanceof WTrapError);
checkFail(
() => callFunction(program, "foo3", [], []),
e => e instanceof WTrapError);
}
tests.swizzle = function()
{
let program = doPrep(`
float foo() {
float4 bar = float4(3., 4., 5., 6.);
float3 baz = bar.zzx;
return baz.z;
}
float foo2() {
float4 bar = float4(3., 4., 5., 6.);
float3 baz = bar.wyz;
return baz.x;
}
float foo3() {
float3 bar = float3(3., 4., 5.);
float2 baz = bar.yz;
float4 quix = baz.yyxx;
return quix.z;
}
`);
checkFloat(program, callFunction(program, "foo", [], []), 3);
checkFloat(program, callFunction(program, "foo2", [], []), 6);
checkFloat(program, callFunction(program, "foo3", [], []), 4);
}
tests.enumWithExplicitIntBase = function()
{
let program = doPrep(`
enum Foo : int {
War,
Famine,
Pestilence,
Death
}
Foo war()
{
return Foo.War;
}
Foo famine()
{
return Foo.Famine;
}
Foo pestilence()
{
return Foo.Pestilence;
}
Foo death()
{
return Foo.Death;
}
bool equals(Foo a, Foo b)
{
return a == b;
}
bool notEquals(Foo a, Foo b)
{
return a != b;
}
bool testSimpleEqual()
{
return equals(Foo.War, Foo.War);
}
bool testAnotherEqual()
{
return equals(Foo.Pestilence, Foo.Pestilence);
}
bool testNotEqual()
{
return equals(Foo.Famine, Foo.Death);
}
bool testSimpleNotEqual()
{
return notEquals(Foo.War, Foo.War);
}
bool testAnotherNotEqual()
{
return notEquals(Foo.Pestilence, Foo.Pestilence);
}
bool testNotNotEqual()
{
return notEquals(Foo.Famine, Foo.Death);
}
int intWar()
{
return int(war());
}
int intFamine()
{
return int(famine());
}
int intPestilence()
{
return int(pestilence());
}
int intDeath()
{
return int(death());
}
int warValue()
{
return war().value;
}
int famineValue()
{
return famine().value;
}
int pestilenceValue()
{
return pestilence().value;
}
int deathValue()
{
return death().value;
}
int warValueLiteral()
{
return Foo.War.value;
}
int famineValueLiteral()
{
return Foo.Famine.value;
}
int pestilenceValueLiteral()
{
return Foo.Pestilence.value;
}
int deathValueLiteral()
{
return Foo.Death.value;
}
Foo intWarBackwards()
{
return Foo(intWar());
}
Foo intFamineBackwards()
{
return Foo(intFamine());
}
Foo intPestilenceBackwards()
{
return Foo(intPestilence());
}
Foo intDeathBackwards()
{
return Foo(intDeath());
}
`);
checkEnum(program, callFunction(program, "war", [], []), 0);
checkEnum(program, callFunction(program, "famine", [], []), 1);
checkEnum(program, callFunction(program, "pestilence", [], []), 2);
checkEnum(program, callFunction(program, "death", [], []), 3);
checkBool(program, callFunction(program, "testSimpleEqual", [], []), true);
checkBool(program, callFunction(program, "testAnotherEqual", [], []), true);
checkBool(program, callFunction(program, "testNotEqual", [], []), false);
checkBool(program, callFunction(program, "testSimpleNotEqual", [], []), false);
checkBool(program, callFunction(program, "testAnotherNotEqual", [], []), false);
checkBool(program, callFunction(program, "testNotNotEqual", [], []), true);
checkInt(program, callFunction(program, "intWar", [], []), 0);
checkInt(program, callFunction(program, "intFamine", [], []), 1);
checkInt(program, callFunction(program, "intPestilence", [], []), 2);
checkInt(program, callFunction(program, "intDeath", [], []), 3);
checkInt(program, callFunction(program, "warValue", [], []), 0);
checkInt(program, callFunction(program, "famineValue", [], []), 1);
checkInt(program, callFunction(program, "pestilenceValue", [], []), 2);
checkInt(program, callFunction(program, "deathValue", [], []), 3);
checkInt(program, callFunction(program, "warValueLiteral", [], []), 0);
checkInt(program, callFunction(program, "famineValueLiteral", [], []), 1);
checkInt(program, callFunction(program, "pestilenceValueLiteral", [], []), 2);
checkInt(program, callFunction(program, "deathValueLiteral", [], []), 3);
checkEnum(program, callFunction(program, "intWarBackwards", [], []), 0);
checkEnum(program, callFunction(program, "intFamineBackwards", [], []), 1);
checkEnum(program, callFunction(program, "intPestilenceBackwards", [], []), 2);
checkEnum(program, callFunction(program, "intDeathBackwards", [], []), 3);
}
tests.enumWithUintBase = function()
{
let program = doPrep(`
enum Foo : uint {
War,
Famine,
Pestilence,
Death
}
Foo war()
{
return Foo.War;
}
Foo famine()
{
return Foo.Famine;
}
Foo pestilence()
{
return Foo.Pestilence;
}
Foo death()
{
return Foo.Death;
}
bool equals(Foo a, Foo b)
{
return a == b;
}
bool notEquals(Foo a, Foo b)
{
return a != b;
}
bool testSimpleEqual()
{
return equals(Foo.War, Foo.War);
}
bool testAnotherEqual()
{
return equals(Foo.Pestilence, Foo.Pestilence);
}
bool testNotEqual()
{
return equals(Foo.Famine, Foo.Death);
}
bool testSimpleNotEqual()
{
return notEquals(Foo.War, Foo.War);
}
bool testAnotherNotEqual()
{
return notEquals(Foo.Pestilence, Foo.Pestilence);
}
bool testNotNotEqual()
{
return notEquals(Foo.Famine, Foo.Death);
}
uint uintWar()
{
return uint(war());
}
uint uintFamine()
{
return uint(famine());
}
uint uintPestilence()
{
return uint(pestilence());
}
uint uintDeath()
{
return uint(death());
}
uint warValue()
{
return war().value;
}
uint famineValue()
{
return famine().value;
}
uint pestilenceValue()
{
return pestilence().value;
}
uint deathValue()
{
return death().value;
}
uint warValueLiteral()
{
return Foo.War.value;
}
uint famineValueLiteral()
{
return Foo.Famine.value;
}
uint pestilenceValueLiteral()
{
return Foo.Pestilence.value;
}
uint deathValueLiteral()
{
return Foo.Death.value;
}
Foo uintWarBackwards()
{
return Foo(uintWar());
}
Foo uintFamineBackwards()
{
return Foo(uintFamine());
}
Foo uintPestilenceBackwards()
{
return Foo(uintPestilence());
}
Foo uintDeathBackwards()
{
return Foo(uintDeath());
}
`);
checkEnum(program, callFunction(program, "war", [], []), 0);
checkEnum(program, callFunction(program, "famine", [], []), 1);
checkEnum(program, callFunction(program, "pestilence", [], []), 2);
checkEnum(program, callFunction(program, "death", [], []), 3);
checkBool(program, callFunction(program, "testSimpleEqual", [], []), true);
checkBool(program, callFunction(program, "testAnotherEqual", [], []), true);
checkBool(program, callFunction(program, "testNotEqual", [], []), false);
checkBool(program, callFunction(program, "testSimpleNotEqual", [], []), false);
checkBool(program, callFunction(program, "testAnotherNotEqual", [], []), false);
checkBool(program, callFunction(program, "testNotNotEqual", [], []), true);
checkUint(program, callFunction(program, "uintWar", [], []), 0);
checkUint(program, callFunction(program, "uintFamine", [], []), 1);
checkUint(program, callFunction(program, "uintPestilence", [], []), 2);
checkUint(program, callFunction(program, "uintDeath", [], []), 3);
checkUint(program, callFunction(program, "warValue", [], []), 0);
checkUint(program, callFunction(program, "famineValue", [], []), 1);
checkUint(program, callFunction(program, "pestilenceValue", [], []), 2);
checkUint(program, callFunction(program, "deathValue", [], []), 3);
checkUint(program, callFunction(program, "warValueLiteral", [], []), 0);
checkUint(program, callFunction(program, "famineValueLiteral", [], []), 1);
checkUint(program, callFunction(program, "pestilenceValueLiteral", [], []), 2);
checkUint(program, callFunction(program, "deathValueLiteral", [], []), 3);
checkEnum(program, callFunction(program, "uintWarBackwards", [], []), 0);
checkEnum(program, callFunction(program, "uintFamineBackwards", [], []), 1);
checkEnum(program, callFunction(program, "uintPestilenceBackwards", [], []), 2);
checkEnum(program, callFunction(program, "uintDeathBackwards", [], []), 3);
}
tests.enumFloatBase = function()
{
checkFail(
() => doPrep(`
enum Foo : float {
Bar
}
`),
e => e instanceof WTypeError);
}
tests.enumPtrBase = function()
{
checkFail(
() => doPrep(`
enum Foo : thread int* {
Bar
}
`),
e => e instanceof WTypeError);
}
tests.enumArrayRefBase = function()
{
checkFail(
() => doPrep(`
enum Foo : thread int[] {
Bar
}
`),
e => e instanceof WTypeError);
}
tests.emptyStruct = function()
{
let program = doPrep(`
struct Thingy { }
int foo()
{
Thingy thingy;
return 46;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 46);
}
tests.enumStructBase = function()
{
checkFail(
() => doPrep(`
struct Thingy { }
enum Foo : Thingy {
Bar
}
`),
e => e instanceof WTypeError);
}
tests.enumNoMembers = function()
{
checkFail(
() => doPrep(`
enum Foo { }
`),
e => e instanceof WTypeError);
}
tests.simpleSwitch = function()
{
let program = doPrep(`
int foo(int x)
{
switch (x) {
case 767:
return 27;
case 69:
return 7624;
default:
return 49;
}
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
}
tests.exhaustiveUint8Switch = function()
{
let text = "double foo(uint8 x) { switch (uint8(x)) {"
for (let i = 0; i <= 0xff; ++i)
text += "case " + i + ": return " + i * 1.5 + ";";
text += "} }";
let program = doPrep(text);
for (let i = 0; i < 0xff; ++i)
checkDouble(program, callFunction(program, "foo", [], [makeUint8(program, i)]), i * 1.5);
}
tests.notQuiteExhaustiveUint8Switch = function()
{
let text = "double foo(uint8 x) { switch (uint8(x)) {"
for (let i = 0; i <= 0xfe; ++i)
text += "case " + i + ": return " + i * 1.5 + ";";
text += "} }";
checkFail(() => doPrep(text), e => e instanceof WTypeError);
}
tests.notQuiteExhaustiveUint8SwitchWithDefault = function()
{
let text = "double foo(uint8 x) { switch (uint8(x)) {"
for (let i = 0; i <= 0xfe; ++i)
text += "case " + i + ": return " + i * 1.5 + ";";
text += "default: return " + 0xff * 1.5 + ";";
text += "} }";
let program = doPrep(text);
for (let i = 0; i < 0xff; ++i)
checkDouble(program, callFunction(program, "foo", [], [makeUint8(program, i)]), i * 1.5);
}
tests.switchFallThrough = function()
{
// FIXME: This might become an error in future versions.
// https://bugs.webkit.org/show_bug.cgi?id=177172
let program = doPrep(`
int foo(int x)
{
int result = 0;
switch (x) {
case 767:
result += 27;
case 69:
result += 7624;
default:
result += 49;
}
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27 + 7624 + 49);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624 + 49);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
}
tests.switchBreak = function()
{
let program = doPrep(`
int foo(int x)
{
int result = 0;
switch (x) {
case 767:
result += 27;
break;
case 69:
result += 7624;
break;
default:
result += 49;
break;
}
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624);
checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
}
tests.enumSwitchBreakExhaustive = function()
{
let program = doPrep(`
enum Foo {
A, B, C
}
int foo(Foo x)
{
int result = 0;
switch (x) {
case Foo.A:
result += 27;
break;
case Foo.B:
result += 7624;
break;
case Foo.C:
result += 49;
break;
}
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "A")]), 27);
checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "B")]), 7624);
checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "C")]), 49);
}
tests.enumSwitchBreakNotQuiteExhaustive = function()
{
checkFail(
() => doPrep(`
enum Foo {
A, B, C, D
}
int foo(Foo x)
{
int result = 0;
switch (x) {
case Foo.A:
result += 27;
break;
case Foo.B:
result += 7624;
break;
case Foo.C:
result += 49;
break;
}
return result;
}
`),
e => e instanceof WTypeError);
}
tests.enumSwitchBreakNotQuiteExhaustiveWithDefault = function()
{
let program = doPrep(`
enum Foo {
A, B, C
}
int foo(Foo x)
{
int result = 0;
switch (x) {
case Foo.A:
result += 27;
break;
case Foo.B:
result += 7624;
break;
default:
result += 49;
break;
}
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "A")]), 27);
checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "B")]), 7624);
checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "C")]), 49);
}
tests.simpleRecursiveStruct = function()
{
checkFail(
() => doPrep(`
struct Foo {
Foo foo;
}
`),
e => e instanceof WTypeError);
}
tests.mutuallyRecursiveStruct = function()
{
checkFail(
() => doPrep(`
struct Foo {
Bar bar;
}
struct Bar {
Foo foo;
}
`),
e => e instanceof WTypeError);
}
tests.mutuallyRecursiveStructWithPointersBroken = function()
{
let program = doPrep(`
struct Foo {
thread Bar* bar;
int foo;
}
struct Bar {
thread Foo* foo;
int bar;
}
int foo()
{
Foo foo;
Bar bar;
foo.foo = 564;
bar.bar = 53;
return foo.bar->bar - bar.foo->foo;
}
`);
checkFail(
() => checkInt(program, callFunction(program, "foo", [], []), -511),
e => e instanceof WTrapError);
}
tests.mutuallyRecursiveStructWithPointers = function()
{
let program = doPrep(`
struct Foo {
thread Bar* bar;
int foo;
}
struct Bar {
thread Foo* foo;
int bar;
}
int foo()
{
Foo foo;
Bar bar;
foo.bar = &bar;
bar.foo = &foo;
foo.foo = 564;
bar.bar = 53;
return foo.bar->bar - bar.foo->foo;
}
`);
checkInt(program, callFunction(program, "foo", [], []), -511);
}
tests.linkedList = function()
{
let program = doPrep(`
struct Node {
thread Node* next;
int value;
}
int foo()
{
Node x, y, z;
x.next = &y;
y.next = &z;
x.value = 1;
y.value = 2;
z.value = 3;
return x.next->next->value;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 3);
}
tests.pointerToPointer = function()
{
let program = doPrep(`
int foo()
{
int x;
thread int* p = &x;
thread int** pp = &p;
int*thread*thread qq = pp;
int result = 0;
x = 42;
*p = 76;
result += x;
**pp = 39;
result += x;
**qq = 83;
result += x;
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 76 + 39 + 83);
}
tests.arrayRefToArrayRef = function()
{
let program = doPrep(`
int foo()
{
int x;
thread int[] p = @x;
thread int[][] pp = @p;
int[]thread[]thread qq = pp;
int result = 0;
x = 42;
p[0] = 76;
result += x;
pp[0][0] = 39;
result += x;
qq[0][0] = 83;
result += x;
return result;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 76 + 39 + 83);
}
tests.pointerGetter = function()
{
checkFail(
() => doPrep(`
int operator.foo(device int*)
{
return 543;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator.foo(thread int*)
{
return 543;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator.foo(threadgroup int*)
{
return 543;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator.foo(constant int*)
{
return 543;
}
`),
e => e instanceof WTypeError);
}
tests.loneSetter = function()
{
checkFail(
() => doPrep(`
int operator.foo=(int, int)
{
return 543;
}
`),
e => e instanceof WTypeError);
}
tests.setterWithMismatchedType = function()
{
checkFail(
() => doPrep(`
double operator.foo(int)
{
return 5.43;
}
int operator.foo=(int, int)
{
return 543;
}
`),
e => e instanceof WTypeError);
}
tests.setterWithMatchedType = function()
{
doPrep(`
int operator.foo(int)
{
return 5;
}
int operator.foo=(int, int)
{
return 543;
}
`);
}
tests.operatorWithUninferrableTypeVariable = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
}
Foo operator+<T>(Foo a, Foo b)
{
Foo result;
result.x = a.x + b.x;
return result;
}
`),
e => e instanceof WTypeError);
}
tests.operatorWithoutUninferrableTypeVariable = function()
{
let program = doPrep(`
struct Foo {
int x;
}
Foo operator+(Foo a, Foo b)
{
Foo result;
result.x = a.x + b.x;
return result;
}
int foo()
{
Foo a;
a.x = 645;
Foo b;
b.x = -35;
return (a + b).x;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 645 - 35);
}
tests.operatorCastWithUninferrableTypeVariable = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
}
operator<T> Foo(int x)
{
Foo result;
result.x = x;
return result;
}
`),
e => e instanceof WTypeError);
}
tests.operatorCastWithTypeVariableInferredFromReturnType = function()
{
let program = doPrep(`
struct Foo {
int x;
}
protocol Barable {
void bar(thread Barable*, int);
}
void bar(thread double* result, int value)
{
*result = double(value);
}
operator<T:Barable> T(Foo foo)
{
T result;
bar(&result, foo.x);
return result;
}
int foo()
{
Foo foo;
foo.x = 75;
double x = double(foo);
return int(x * 1.5);
}
`);
checkInt(program, callFunction(program, "foo", [], []), 112);
}
tests.incWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator++() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator++(int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.decWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator--() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator--(int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.incWrongTypes = function()
{
checkFail(
() => doPrep(`
int operator++(double) { return 32; }
`),
e => e instanceof WTypeError);
}
tests.decWrongTypes = function()
{
checkFail(
() => doPrep(`
int operator--(double) { return 32; }
`),
e => e instanceof WTypeError);
}
tests.plusWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator+() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator+(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.minusWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator-() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator-(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.timesWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator*() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator*(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator*(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.divideWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator/() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator/(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator/(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.moduloWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator%() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator%(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator%(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.bitAndWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator&() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator&(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator&(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.bitOrWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator|() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator|(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator|(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.bitXorWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator^() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator^(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator^(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.lShiftWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator<<() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator<<(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator<<(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.rShiftWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator>>() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator>>(int) { return 534; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator>>(int, int, int) { return 76; }
`),
e => e instanceof WTypeError);
}
tests.bitNotWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator~() { return 32; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator~(int, int) { return 534; }
`),
e => e instanceof WTypeError);
}
tests.equalsWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
bool operator==() { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator==(int) { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator==(int, int, int) { return true; }
`),
e => e instanceof WTypeError);
}
tests.lessThanWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
bool operator<() { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator<(int) { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator<(int, int, int) { return true; }
`),
e => e instanceof WTypeError);
}
tests.lessEqualWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
bool operator<=() { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator<=(int) { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator<=(int, int, int) { return true; }
`),
e => e instanceof WTypeError);
}
tests.greaterWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
bool operator>() { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator>(int) { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator>(int, int, int) { return true; }
`),
e => e instanceof WTypeError);
}
tests.greaterEqualWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
bool operator>=() { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator>=(int) { return true; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
bool operator>=(int, int, int) { return true; }
`),
e => e instanceof WTypeError);
}
tests.equalsWrongReturnType = function()
{
checkFail(
() => doPrep(`
int operator==(int a, int b) { return a + b; }
`),
e => e instanceof WTypeError);
}
tests.notEqualsOverload = function()
{
checkFail(
() => doPrep(`
struct Foo { }
bool operator!=(Foo, Foo) { return true; }
`),
e => e instanceof WSyntaxError);
}
tests.lessThanWrongReturnType = function()
{
checkFail(
() => doPrep(`
int operator<(int a, int b) { return a + b; }
`),
e => e instanceof WTypeError);
}
tests.lessEqualWrongReturnType = function()
{
checkFail(
() => doPrep(`
int operator<=(int a, int b) { return a + b; }
`),
e => e instanceof WTypeError);
}
tests.greaterThanWrongReturnType = function()
{
checkFail(
() => doPrep(`
int operator>(int a, int b) { return a + b; }
`),
e => e instanceof WTypeError);
}
tests.greaterEqualWrongReturnType = function()
{
checkFail(
() => doPrep(`
int operator>=(int a, int b) { return a + b; }
`),
e => e instanceof WTypeError);
}
tests.dotOperatorWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator.foo() { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo { }
int operator.foo(Foo, int) { return 42; }
`),
e => e instanceof WTypeError);
}
tests.dotOperatorSetterWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
struct Foo { }
Foo operator.foo=() { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo { }
Foo operator.foo=(Foo) { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo { }
Foo operator.foo=(Foo, int, int) { return 42; }
`),
e => e instanceof WTypeError);
}
tests.loneSetterPointer = function()
{
checkFail(
() => doPrep(`
thread int* operator.foo=(thread int* ptr, int)
{
return ptr;
}
`),
e => e instanceof WTypeError);
}
tests.setterWithNoGetterOverload = function()
{
checkFail(
() => doPrep(`
struct Foo { }
struct Bar { }
int operator.foo(Foo)
{
return 534;
}
Bar operator.foo=(Bar, int)
{
return Bar();
}
`),
e => e instanceof WTypeError);
}
tests.setterWithNoGetterOverloadFixed = function()
{
doPrep(`
struct Bar { }
int operator.foo(Bar)
{
return 534;
}
Bar operator.foo=(Bar, int)
{
return Bar();
}
`);
}
tests.anderWithNothingWrong = function()
{
let program = doPrep(`
struct Foo {
int x;
}
thread int* operator&.foo(thread Foo* foo)
{
return &foo->x;
}
int foo()
{
Foo x;
x.x = 13;
return x.foo;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 13);
}
tests.anderWithWrongNumberOfArguments = function()
{
checkFail(
() => doPrep(`
thread int* operator&.foo()
{
int x;
return &x;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
int x;
}
thread int* operator&.foo(thread Foo* foo, int blah)
{
return &foo->x;
}
`),
e => e instanceof WTypeError);
}
tests.anderDoesntReturnPointer = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
}
int operator&.foo(thread Foo* foo)
{
return foo->x;
}
`),
e => e instanceof WTypeError);
}
tests.anderDoesntTakeReference = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
}
thread int* operator&.foo(Foo foo)
{
return &foo.x;
}
`),
e => e instanceof WTypeError);
}
tests.anderWithArrayRef = function()
{
let program = doPrep(`
struct Foo {
int x;
}
thread int* operator&.foo(thread Foo[] foo)
{
return &foo[0].x;
}
int foo()
{
Foo x;
x.x = 13;
return (@x).foo;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 13);
}
tests.pointerIndexGetter = function()
{
checkFail(
() => doPrep(`
int operator[](device int*, uint)
{
return 543;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[](thread int*, uint)
{
return 543;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[](threadgroup int*, uint)
{
return 543;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[](constant int*, uint)
{
return 543;
}
`),
e => e instanceof WTypeError);
}
tests.loneIndexSetter = function()
{
checkFail(
() => doPrep(`
int operator[]=(int, uint, int)
{
return 543;
}
`),
e => e instanceof WTypeError);
}
tests.notLoneIndexSetter = function()
{
doPrep(`
int operator[](int, uint)
{
return 65;
}
int operator[]=(int, uint, int)
{
return 543;
}
`);
}
tests.indexSetterWithMismatchedType = function()
{
checkFail(
() => doPrep(`
double operator[](int, uint)
{
return 5.43;
}
int operator[]=(int, uint, int)
{
return 543;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Setter and getter must agree on value type") != -1);
}
tests.indexOperatorWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator[]() { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[](int) { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[](int, int, int) { return 42; }
`),
e => e instanceof WTypeError);
}
tests.indexOperatorSetterWrongArgumentLength = function()
{
checkFail(
() => doPrep(`
int operator[]=() { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[]=(int) { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[]=(int, int) { return 42; }
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
int operator[]=(int, int, int, int) { return 42; }
`),
e => e instanceof WTypeError);
}
tests.loneIndexSetterPointer = function()
{
checkFail(
() => doPrep(`
thread int* operator[]=(thread int* ptr, uint, int)
{
return ptr;
}
`),
e => e instanceof WTypeError);
}
tests.indexSetterWithNoGetterOverload = function()
{
checkFail(
() => doPrep(`
struct Foo { }
struct Bar { }
int operator[](Foo, uint)
{
return 534;
}
Bar operator[]=(Bar, uint, int)
{
return Bar();
}
`),
e => e instanceof WTypeError);
}
tests.indexSetterWithNoGetterOverloadFixed = function()
{
doPrep(`
struct Bar { }
int operator[](Bar, uint)
{
return 534;
}
Bar operator[]=(Bar, uint, int)
{
return Bar();
}
`);
}
tests.indexAnderWithNothingWrong = function()
{
let program = doPrep(`
struct Foo {
int x;
}
thread int* operator&[](thread Foo* foo, uint)
{
return &foo->x;
}
int foo()
{
Foo x;
x.x = 13;
return x[666];
}
`);
checkInt(program, callFunction(program, "foo", [], []), 13);
}
tests.indexAnderWithWrongNumberOfArguments = function()
{
checkFail(
() => doPrep(`
thread int* operator&[]()
{
int x;
return &x;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
int x;
}
thread int* operator&[](thread Foo* foo)
{
return &foo->x;
}
`),
e => e instanceof WTypeError);
checkFail(
() => doPrep(`
struct Foo {
int x;
}
thread int* operator&[](thread Foo* foo, uint, uint)
{
return &foo->x;
}
`),
e => e instanceof WTypeError);
}
tests.indexAnderDoesntReturnPointer = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
}
int operator&[](thread Foo* foo, uint)
{
return foo->x;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Return type of ander is not a pointer") != -1);
}
tests.indexAnderDoesntTakeReference = function()
{
checkFail(
() => doPrep(`
struct Foo {
int x;
}
thread int* operator&[](Foo foo, uint)
{
return &foo.x;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Parameter to ander is not a reference") != -1);
}
tests.indexAnderWithArrayRef = function()
{
let program = doPrep(`
struct Foo {
int x;
}
thread int* operator&[](thread Foo[] array, double index)
{
return &array[uint(index + 1)].x;
}
int foo()
{
Foo x;
x.x = 13;
return (@x)[double(-1)];
}
`);
checkInt(program, callFunction(program, "foo", [], []), 13);
}
tests.devicePtrPtr = function()
{
checkFail(
() => doPrep(`
void foo()
{
device int** p;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Illegal pointer to non-primitive type: int32* device* device") != -1);
}
tests.threadgroupPtrPtr = function()
{
checkFail(
() => doPrep(`
void foo()
{
threadgroup int** p;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Illegal pointer to non-primitive type: int32* threadgroup* threadgroup") != -1);
}
tests.constantPtrPtr = function()
{
checkFail(
() => doPrep(`
void foo()
{
constant int** p;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Illegal pointer to non-primitive type: int32* constant* constant") != -1);
}
tests.pointerIndexGetterInProtocol = function()
{
for (let addressSpace of addressSpaces) {
checkFail(
() => doPrep(`
protocol Foo {
int operator[](${addressSpace} Foo*, uint);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
`),
e => e instanceof WTypeError && e.message.indexOf("Cannot have getter for pointer type") != -1);
}
}
tests.loneIndexSetterInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
Foo operator[]=(Foo, uint, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Every setter must have a matching getter") != -1);
}
tests.notLoneIndexSetterInProtocol = function()
{
doPrep(`
protocol Foo {
int operator[](Foo, uint);
Foo operator[]=(Foo, uint, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`);
}
tests.indexSetterWithMismatchedTypeInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
double operator[](Foo, uint);
Foo operator[]=(Foo, uint, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Setter and getter must agree on value type") != -1);
}
tests.indexOperatorWrongArgumentLengthInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
int operator[]();
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
`),
e => e instanceof WTypeError && e.message.indexOf("Protocol's type variable (Foo) not mentioned in signature") != -1);
checkFail(
() => doPrep(`
protocol Foo {
int operator[](Foo);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters") != -1);
checkFail(
() => doPrep(`
protocol Foo {
int operator[](Foo, int, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters") != -1);
}
tests.indexOperatorSetterWrongArgumentLengthInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
int operator[]=();
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Protocol's type variable (Foo) not mentioned in signature") != -1);
checkFail(
() => doPrep(`
protocol Foo {
int operator[]=(Foo);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters") != -1);
checkFail(
() => doPrep(`
protocol Foo {
int operator[]=(Foo, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters") != -1);
checkFail(
() => doPrep(`
protocol Foo {
int operator[]=(Foo, int, int, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters") != -1);
}
tests.loneIndexSetterPointerInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
thread int* operator[]=(thread Foo* ptr, uint, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Cannot have setter for pointer type") != -1);
}
tests.indexSetterWithNoGetterOverloadInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
int operator[](int, Foo);
Foo operator[]=(Foo, uint, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`),
e => e instanceof WTypeError && e.message.indexOf("Did not find function named operator[]= with arguments Foo,uint32") != -1);
}
tests.indexSetterWithNoGetterOverloadFixedInProtocol = function()
{
doPrep(`
protocol Foo {
int operator[](Foo, uint);
Foo operator[]=(Foo, uint, int);
}
struct Bar { }
int operator[](Bar, uint) { return 42; }
Bar operator[]=(Bar, uint, int) { return Bar(); }
`);
}
tests.indexAnderWithNothingWrongInProtocol = function()
{
let program = doPrep(`
protocol Foo {
thread int* operator&[](thread Foo* foo, uint);
}
int bar<T:Foo>(T x)
{
return x[42];
}
struct Bar { }
thread int* operator&[](thread Bar*, uint)
{
int result = 1234;
return &result;
}
int foo()
{
return bar(Bar());
}
`);
checkInt(program, callFunction(program, "foo", [], []), 1234);
}
tests.indexAnderWithWrongNumberOfArgumentsInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
thread int* operator&[]();
}
struct Bar { }
thread int* operator&[](thread Bar*, uint)
{
int result = 1234;
return &result;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Protocol's type variable (Foo) not mentioned in signature") != -1);
checkFail(
() => doPrep(`
protocol Foo {
thread int* operator&[](thread Foo* foo);
}
struct Bar { }
thread int* operator&[](thread Bar*, uint)
{
int result = 1234;
return &result;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters for operator&[]") != -1);
checkFail(
() => doPrep(`
protocol Foo {
thread int* operator&[](thread Foo* foo, uint, uint);
}
struct Bar { }
thread int* operator&[](thread Bar*, uint)
{
int result = 1234;
return &result;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Incorrect number of parameters for operator&[]") != -1);
}
tests.indexAnderDoesntReturnPointerInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
int operator&[](thread Foo* foo, uint);
}
struct Bar { }
thread int* operator&[](thread Bar*, uint)
{
int result = 1234;
return &result;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Return type of ander is not a pointer") != -1);
}
tests.indexAnderDoesntTakeReferenceInProtocol = function()
{
checkFail(
() => doPrep(`
protocol Foo {
thread int* operator&[](Foo foo, uint);
}
struct Bar { }
thread int* operator&[](thread Bar*, uint)
{
int result = 1234;
return &result;
}
`),
e => e instanceof WTypeError && e.message.indexOf("Parameter to ander is not a reference") != -1);
}
tests.indexAnderWithArrayRefInProtocol = function()
{
let program = doPrep(`
protocol Foo {
thread int* operator&[](thread Foo[] array, double index);
}
int bar<T:Foo>(thread T[] x)
{
return x[1.5];
}
struct Bar { }
thread int* operator&[](thread Bar[], double)
{
int result = 1234;
return &result;
}
int foo()
{
Bar x;
return bar(@x);
}
`);
checkInt(program, callFunction(program, "foo", [], []), 1234);
}
tests.andReturnedArrayRef = function()
{
let program = doPrep(`
thread int[] getArray()
{
int[10] x;
x[5] = 354;
return @x;
}
int foo()
{
thread int* ptr = &getArray()[5];
return *ptr;
}
`);
checkInt(program, callFunction(program, "foo", [], []), 354);
}
okToTest = true;
function doTest()
{
if (!okToTest)
throw new Error("Test setup is incomplete.");
let names = [];
for (let s in tests)
names.push(s);
names.sort();
for (let s of names) {
tests[s]();
}
}
class Benchmark {
buildStdlib() {
prepare();
}
run() {
doTest()
}
}