Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/strands/ir_builders.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,46 @@ export function binaryOpNode(
}
}

const leftDim = dag.dimensions[finalLeftNodeID];
const rightDim = dag.dimensions[finalRightNodeID];
const leftBase = dag.baseTypes[finalLeftNodeID];
const rightBase = dag.baseTypes[finalRightNodeID];

const isOrdering = [
OpCode.Binary.LESS_THAN,
OpCode.Binary.LESS_EQUAL,
OpCode.Binary.GREATER_THAN,
OpCode.Binary.GREATER_EQUAL,
].includes(opCode);
const isEquality = opCode === OpCode.Binary.EQUAL || opCode === OpCode.Binary.NOT_EQUAL;
const isLogical = opCode === OpCode.Binary.LOGICAL_AND || opCode === OpCode.Binary.LOGICAL_OR;

if (isOrdering) {
if (leftDim > 1 || rightDim > 1) {
FES.userError(
'type error',
`${OpCodeToSymbol[opCode]} is only defined for scalars. ` +
`Got ${leftBase}${leftDim} ${OpCodeToSymbol[opCode]} ${rightBase}${rightDim}.`
);
}
} else if (isEquality) {
if ((leftDim > 1 || rightDim > 1) && (leftDim !== rightDim || leftBase !== rightBase)) {
FES.userError(
'type error',
`Equality comparisons between vectors require matching dimensions and base types. ` +
`Got ${leftBase}${leftDim} ${OpCodeToSymbol[opCode]} ${rightBase}${rightDim}.`
);
}
} else if (isLogical) {
if (leftBase !== BaseType.BOOL || rightBase !== BaseType.BOOL || leftDim !== 1 || rightDim !== 1) {
FES.userError(
'type error',
`${OpCodeToSymbol[opCode]} requires two bool scalars. ` +
`Got ${leftBase}${leftDim} ${OpCodeToSymbol[opCode]} ${rightBase}${rightDim}.`
);
}
}

if (booleanOpCode[opCode]) {
cast.toType.baseType = BaseType.BOOL;
cast.toType.dimension = 1;
Expand Down
112 changes: 112 additions & 0 deletions test/unit/webgl/p5.Shader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3796,6 +3796,118 @@
assert.include(errMsg, 'float4');
});

test('ordering comparison with a vector operand throws a clear strands type error', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);

try {
myp5.baseMaterialShader().modify(
() => {
myp5.getFinalColor(color => {
if (color < 2) {
color = [1, 1, 1, 1];
}
return color;
});
},
{ myp5 }
);
} catch (e) {

Check warning on line 3814 in test/unit/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / lint

eslint(no-unused-vars)

test/unit/webgl/p5.Shader.js:3814:16: Catch parameter 'e' is caught but never used.
/* expected */
}

assert.isAbove(
mockUserError.mock.calls.length,
0,
'FES.userError should have been called'
);
const errMsg = mockUserError.mock.calls[0][1];
assert.include(errMsg, '<');
assert.include(errMsg, 'only defined for scalars');
});

test('ordering comparison between scalars is allowed', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);

myp5.baseMaterialShader().modify(
() => {
myp5.getFinalColor(color => {
if (color.r < 0.5) {
color = [1, 1, 1, 1];
}
return color;
});
},
{ myp5 }
);

assert.equal(mockUserError.mock.calls.length, 0);
});

test('equality comparison between matching vectors is allowed', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);

myp5.baseMaterialShader().modify(
() => {
myp5.getFinalColor(color => {
if (color.equalTo([1, 1, 1, 1])) {
color = [1, 1, 1, 1];
}
return color;
});
},
{ myp5 }
);

assert.equal(mockUserError.mock.calls.length, 0);
});

test('logical and with non-boolean operands throws a clear strands type error', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);

try {
myp5.baseMaterialShader().modify(
() => {
myp5.getFinalColor(color => {
if (color.r && color.g) {
color = [1, 1, 1, 1];
}
return color;
});
},
{ myp5 }
);
} catch (e) {

Check warning on line 3879 in test/unit/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / lint

eslint(no-unused-vars)

test/unit/webgl/p5.Shader.js:3879:16: Catch parameter 'e' is caught but never used.
/* expected */
}

assert.isAbove(
mockUserError.mock.calls.length,
0,
'FES.userError should have been called'
);
const errMsg = mockUserError.mock.calls[0][1];
assert.include(errMsg, '&&');
assert.include(errMsg, 'requires two bool scalars');
});

test('logical and between two boolean scalars is allowed', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);

myp5.baseMaterialShader().modify(
() => {
myp5.getFinalColor(color => {
if (color.r < 0.5 && color.g > 0.5) {
color = [1, 1, 1, 1];
}
return color;
});
},
{ myp5 }
);

assert.equal(mockUserError.mock.calls.length, 0);
});

test('shows a helpful error for web editor loop protection', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);

Expand Down
Loading