Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comparison with named integer type fails #632

Open
mbertschler opened this issue Apr 22, 2024 · 1 comment
Open

Comparison with named integer type fails #632

mbertschler opened this issue Apr 22, 2024 · 1 comment

Comments

@mbertschler
Copy link

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)
	})
}
@antonmedv
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants