expr
expr copied to clipboard
Comparison with named integer type fails
Hey @antonmedv. Thanks for this amazing library, it is a lot of fun to work with.
After deciding to use it for a new feature with dynamic configuration, we unfortunately ran into some unexpected behavior. The problem is that the expression Named == 4
returns false
with this environment definition:
type NamedUint uint32
type Env struct {
Named NamedUint
}
env := &Env{Named: 4}
It seems to be because we use a named type NamedUint
instead of the basic type uint32
in our struct.
Is this behavior expected?
Reproducing Test
Version: github.com/expr-lang/expr v1.16.1
func TestExprUint(t *testing.T) {
type NamedUint uint32
type Env struct {
Plain uint32
Named NamedUint
}
testEnv := &Env{
Plain: 3,
Named: 4,
}
evaluateCode := func(t *testing.T, code string, expected interface{}) {
program, err := expr.Compile(code, expr.Env(&Env{}))
if err != nil {
t.Error("Compile", err)
}
output, err := expr.Run(program, testEnv)
if err != nil {
t.Error("Run", err)
}
formatted := fmt.Sprintf("%v", output)
if formatted != fmt.Sprintf("%v", expected) {
t.Errorf("expected %v, got %v. Code: %q Env: %+v", expected, output, code, testEnv)
}
}
t.Run("Plain return", func(t *testing.T) {
code := `Plain`
evaluateCode(t, code, 3)
})
t.Run("Named return", func(t *testing.T) {
code := `Named`
evaluateCode(t, code, 4)
})
t.Run("Plain equal", func(t *testing.T) {
code := `Plain == 3`
evaluateCode(t, code, true)
})
// --- FAIL: TestExprUint (0.00s)
// --- FAIL: TestExprUint/Named_equal (0.00s)
// skan_assignment_test.go:394: expected true, got false. Code: "Named == 4" Env: &{Plain:3 Named:4}
t.Run("Named equal", func(t *testing.T) {
code := `Named == 4`
evaluateCode(t, code, true)
})
}
Hey @mbertschler, thanks!
I understand this problem. This is actually a Golang related one:
https://go.dev/play/p/nYw6xmQ9Ll2
package main
import (
"fmt"
"reflect"
)
type MyInt int
func main() {
var my MyInt = 1
var i int = 1
fmt.Println(my == i) // Compilation will fail.
fmt.Println(reflect.DeepEqual(my, i)) // Will return false.
}
Recently in #611 we improved int()
builtin function to unwrap custom int types:
int(Named) == 4 // This will work in Expr.
One thing you can do is to write a patcher which will wrap all custom ints with int(...)
call.