diff --git a/internal/shader/stmt.go b/internal/shader/stmt.go index 508635d2b..3a3f6aeb1 100644 --- a/internal/shader/stmt.go +++ b/internal/shader/stmt.go @@ -87,25 +87,38 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP } stmts = append(stmts, ss...) - if op == shaderir.ModOp { - if lts[0].Main != shaderir.Int || rts[0].Main != shaderir.Int { - var wrongType shaderir.Type - if lts[0].Main != shaderir.Int { - wrongType = lts[0] - } else { - wrongType = rts[0] - } - cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %% not defined on %s", wrongType.String())) - return nil, false - } - } - + // Treat an integer literal as an integer constant value. if rhs[0].Type == shaderir.NumberExpr && rts[0].Main == shaderir.Int { if !cs.forceToInt(stmt, &rhs[0]) { return nil, false } } + if lts[0].Main != rts[0].Main { + switch lts[0].Main { + case shaderir.Int: + if !cs.forceToInt(stmt, &rhs[0]) { + return nil, false + } + case shaderir.Float: + if rhs[0].Const != nil && rhs[0].Const.Kind() == gconstant.Int { + rhs[0].Const = gconstant.ToFloat(rhs[0].Const) + rhs[0].ConstType = shaderir.ConstTypeFloat + } else { + cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: mismatched types %s and %s", lts[0].String(), rts[0].String())) + return nil, false + } + default: + cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: mismatched types %s and %s", lts[0].String(), rts[0].String())) + return nil, false + } + } + + if op == shaderir.ModOp && lts[0].Main != shaderir.Int { + cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %% not defined on %s", lts[0].String())) + return nil, false + } + stmts = append(stmts, shaderir.Stmt{ Type: shaderir.Assign, Exprs: []shaderir.Expr{ diff --git a/internal/shader/testdata/for.expected.vs b/internal/shader/testdata/for.expected.vs index 9cb3f640c..b57a48444 100644 --- a/internal/shader/testdata/for.expected.vs +++ b/internal/shader/testdata/for.expected.vs @@ -5,11 +5,11 @@ void F0(out vec2 l0) { vec2 l3 = vec2(0); l1 = vec2(0.0); for (int l2 = 0; l2 < 100; l2++) { - (l1).x = ((l1).x) + (l2); + (l1).x = ((l1).x) + (float(l2)); } l3 = vec2(0.0); for (float l4 = 10.0; l4 >= 0.0; l4 -= 2.0) { - (l3).x = ((l3).x) + (l4); + (l3).x = ((l3).x) + (float(l4)); } l0 = l1; return; diff --git a/internal/shader/testdata/for.go b/internal/shader/testdata/for.go index 393029037..9ea023152 100644 --- a/internal/shader/testdata/for.go +++ b/internal/shader/testdata/for.go @@ -3,11 +3,11 @@ package main func Foo() vec2 { v := vec2(0) for i := 0; i < 100; i++ { - v.x += i + v.x += float(i) } v2 := vec2(0) for i := 10.0; i >= 0; i -= 2 { - v2.x += i + v2.x += float(i) } _ = v2 return v diff --git a/internal/shader/testdata/for2.expected.vs b/internal/shader/testdata/for2.expected.vs index 0fa15b435..252bca9fa 100644 --- a/internal/shader/testdata/for2.expected.vs +++ b/internal/shader/testdata/for2.expected.vs @@ -5,7 +5,7 @@ void F0(out vec2 l0) { vec2 l3 = vec2(0); l1 = vec2(0.0); for (int l2 = 0; l2 < 100; l2++) { - (l1).x = ((l1).x) + (l2); + (l1).x = ((l1).x) + (float(l2)); if (((l1).x) >= (100.0)) { break; } diff --git a/internal/shader/testdata/for2.go b/internal/shader/testdata/for2.go index 5a8a2b53e..faf3644ba 100644 --- a/internal/shader/testdata/for2.go +++ b/internal/shader/testdata/for2.go @@ -3,7 +3,7 @@ package main func Foo() vec2 { v := vec2(0) for i := 0; i < 100; i++ { - v.x += i + v.x += float(i) if v.x >= 100 { break } diff --git a/shader_test.go b/shader_test.go index d30ac4181..0bb9bfca6 100644 --- a/shader_test.go +++ b/shader_test.go @@ -1351,6 +1351,16 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 { if _, err := ebiten.NewShader([]byte(`package main +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 2 + a %= 1.0 + return vec4(a) +}`)); err != nil { + t.Error(err) + } + + if _, err := ebiten.NewShader([]byte(`package main + func Fragment(position vec4, texCoord vec2, color vec4) vec4 { a := 2 a %= 0.5 @@ -1369,3 +1379,65 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 { t.Errorf("error must be non-nil but was nil") } } + +func TestShaderOperatorAssign(t *testing.T) { + if _, err := ebiten.NewShader([]byte(`package main + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 1.0 + a += 2 + return vec4(a) +}`)); err != nil { + t.Error(err) + } + + if _, err := ebiten.NewShader([]byte(`package main + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 1.0 + a += 2.0 + return vec4(a) +}`)); err != nil { + t.Error(err) + } + + if _, err := ebiten.NewShader([]byte(`package main + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 1.0 + a += 2.1 + return vec4(a) +}`)); err != nil { + t.Error(err) + } + + if _, err := ebiten.NewShader([]byte(`package main + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 1 + a += 2 + return vec4(a) +}`)); err != nil { + t.Error(err) + } + + if _, err := ebiten.NewShader([]byte(`package main + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 1 + a += 2.0 + return vec4(a) +}`)); err != nil { + t.Error(err) + } + + if _, err := ebiten.NewShader([]byte(`package main + +func Fragment(position vec4, texCoord vec2, color vec4) vec4 { + a := 1 + a += 2.1 + return vec4(a) +}`)); err == nil { + t.Errorf("error must be non-nil but was nil") + } +}