Skip to content

Commit

Permalink
support automatic conversion of variable types
Browse files Browse the repository at this point in the history
  • Loading branch information
SimFG committed Oct 12, 2024
1 parent 9ca3d59 commit 9f20ab1
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 5 deletions.
6 changes: 3 additions & 3 deletions checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,7 @@ func (v *checker) checkBuiltinGet(node *ast.BuiltinNode) Nature {
}
return base.Elem()
case reflect.Map:
if !prop.AssignableTo(base.Key()) && !isUnknown(prop) {
if !prop.ConvertibleTo(base.Key()) && !isUnknown(prop) {
return v.error(node.Arguments[1], "cannot use %s to get an element from %s", prop, base)
}
return base.Elem()
Expand Down Expand Up @@ -1053,12 +1053,12 @@ func (v *checker) checkArguments(
// Check if argument is assignable to the function input type.
// We check original type (like *time.Time), not dereferenced type,
// as function input type can be pointer to a struct.
assignable := argNature.AssignableTo(in)
assignable := argNature.ConvertibleTo(in)

// We also need to check if dereference arg type is assignable to the function input type.
// For example, func(int) and argument *int. In this case we will add OpDeref to the argument,
// so we can call the function with *int argument.
assignable = assignable || argNature.Deref().AssignableTo(in)
assignable = assignable || argNature.Deref().ConvertibleTo(in)

if !assignable && !isUnknown(argNature) {
return unknown, &file.Error{
Expand Down
13 changes: 13 additions & 0 deletions checker/nature/nature.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ func (n Nature) AssignableTo(nt Nature) bool {
return n.Type.AssignableTo(nt.Type)
}

func (n Nature) ConvertibleTo(nt Nature) bool {
if n.Nil {
// Untyped nil is assignable to any interface, but implements only the empty interface.
if nt.Type != nil && nt.Type.Kind() == reflect.Interface {
return true
}
}
if n.Type == nil || nt.Type == nil {
return false
}
return n.Type.ConvertibleTo(nt.Type)
}

func (n Nature) MethodByName(name string) (Nature, bool) {
if n.Type == nil {
return unknown, false
Expand Down
8 changes: 7 additions & 1 deletion vm/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@ func Fetch(from, i any) any {
if i == nil {
value = v.MapIndex(reflect.Zero(v.Type().Key()))
} else {
value = v.MapIndex(reflect.ValueOf(i))
mapKeyType := v.Type().Key()
requestKeyType := reflect.TypeOf(i)
requestKeyValue := reflect.ValueOf(i)
if mapKeyType != requestKeyType {
requestKeyValue = requestKeyValue.Convert(mapKeyType)
}
value = v.MapIndex(requestKeyValue)
}
if value.IsValid() {
return value.Interface()
Expand Down
8 changes: 7 additions & 1 deletion vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,13 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
// otherwise reflect.Call will panic on zero value.
in[i] = reflect.ValueOf(&param).Elem()
} else {
in[i] = reflect.ValueOf(param)
methodType := fn.Type().In(i)
requestType := reflect.TypeOf(param)
requestValue := reflect.ValueOf(param)
if requestType != methodType {
requestValue = requestValue.Convert(requestType)
}
in[i] = requestValue
}
}
out := fn.Call(in)
Expand Down

0 comments on commit 9f20ab1

Please sign in to comment.