c3c icon indicating copy to clipboard operation
c3c copied to clipboard

Unhandled edge cases for cast/assignment to vector type

Open cbuttner opened this issue 1 year ago • 11 comments

Cast/assignment to vector has some unhandled edge cases:

float[<4>] a;
a = true; // Permitted, but shouldn't be
a = "1234"; // I get this casts char[4] to float[<4>], but can this specific case be prevented for the String type?

// Casting from pointer
a = (void*)1; // No proper error emitted
a = null; // No proper error emitted
a = &foo; // Casting from function pointer, no proper error emitted

cbuttner avatar Jul 01 '24 08:07 cbuttner

Yeah, looks bad.

lerno avatar Jul 01 '24 11:07 lerno

Should be fixed now.

lerno avatar Jul 01 '24 13:07 lerno

Thanks. I think the implicit scalar broadcast cast should be explicit instead. This should better signify intent to actually do a broadcast, and prevent accidentally assigning or passing a scalar where a vector is expected.

cbuttner avatar Jul 04 '24 12:07 cbuttner

That would break things like

int[<3>] x = foo();
int[<3>] y = x * 3 + 1; 
// int[<3>] y = x * (int[<3>])3 + (int[<3>])1;

lerno avatar Jul 04 '24 15:07 lerno

It should break this, scaling is the only operator that makes sense mathematically ("ah yes, let me add 1 to a vector"). I'd rather it be x + (int[<3>])1.

cbuttner avatar Jul 04 '24 16:07 cbuttner

I think this is fine in GLSL though?

lerno avatar Jul 04 '24 17:07 lerno

True, but it's important to note that for GLSL the implicit cast is only legal for operators, meaning you can't assign or pass a scalar to a vector (see https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf Section 5.9). If that were also the case in C3 I'd have no problem with the implicit broadcasting.

cbuttner avatar Jul 04 '24 17:07 cbuttner

If we think about how casts work:

  1. First we have the normal casts, for example between binary sub expression.
  2. Then we have the assign expression, here there is some inference and more leeway.
  3. Finally we have the most generous casts, which is during initialization.

So this is problematic in reversing how "generous" the rules are in the particular case of scalar -> vector conversion. This special case will also likely affect how one would write macros.

For example we would have 1 not being assignable (so $assignable(1, int[<2>]) would fail) to int[<2>] but it would be possible to multiply the scalar with the expression? So that is my concern.

lerno avatar Jul 04 '24 18:07 lerno

For what it's worth, HLSL always does the scalar conversion: https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf

lerno avatar Jul 04 '24 19:07 lerno

I'm still on the fence about this, except for one thing that bothers me the most. I found a solution that works for me though: I added a specific check to my fork which prevents implicit casting function arguments from scalar to vector types. The reason being that type awareness of parameters on the caller side is typically very low. So at least I have a way to enforce this in my codebase.

cbuttner avatar Jul 22 '24 17:07 cbuttner

If you just want it explicit for parameters, then that's something I could consider. I think the conversions in expressions is important, but conversion passing it as arguments.. that's a different thing.

lerno avatar Jul 22 '24 18:07 lerno

The original issue looks fixed.

As for the changes to implicit scalar->vector casting, I feel pretty good about them. They strike a good middle ground between convenience and explicitness.

cbuttner avatar Aug 03 '24 20:08 cbuttner