Skip to content

go/types,cmd/compile/internal/types2: extend types.Info with implicit conversions from assignability #47151

@mdempsky

Description

@mdempsky

Currently, a statement like a = b may require an implicit conversion of b to a's type before the assignment can happen. In particular, for conversions to interface type, these generally require a run-time operation to change the value representation. (An implicit conversion can also happen for assignments between defined types and their underlying type literal or assignments from unrestricted channel types to send/receive-only channel types; but these are more of a formality, since Go implementations in practice use identical value representations for these cases.)

go/types provides enough information for users to infer/reconstruct these implicit conversions, but go/types already has to handle this as part of type checking anyway. So we might as well surface the information to users, so they don't have to remember all of the cases where implicit conversions happen.

My thoughts were along the lines of either:

  1. Add a types.Info.ImplicitTypes map of type map[ast.Expr]types.Type, which will record the type that the expression needs to be implicitly converted, if it differs from types.Info.Types[e].Type. (E.g., for var x int = int(42), int(42) would not need an entry in this map, because int(42) is already the same type as it's assigned to; but var x interface{} = int(42) would have an entry.)
  2. Same idea, but extend types.TypeAndValue with an extra Implicit Type field. Might be simpler/cleaner in some use cases, but it also implies more memory consumption (which I think at least gopls is very sensitive to).

Further, for N:1 assignments, I'd suggest the ImplicitTypes be present and a synthesized Tuple type if any of the respective tuple elements need implicit conversion. For example:

type MyBool bool
var f func() (int, int)
var g func(interface{}, interface{})
var a int
var b interface{}
var x MyBool

a, b = f() // 'f()' has Type (int, int); but ImplicitType (int, interface{}), due to assignment to a,b

g(f()) // 'f()' has Type (int, int); but ImplicitType (interface{}, interface{}), due to passing to g()

b, x = b.(int) // 'b.(int)' has Type (int, MyBool†); but ImplicitType (interface{}, MyBool), due to assignment to b,x
// † cmd/compile's legacy typechecker produces 'MyBool' in this case, but I think 'bool' or 'untyped bool' would be okay too

Incidentally, I think having a way to record separate types for the expression in isolation vs expression in context could help with cases like #13061.

/cc @griesemer @findleyr

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.compiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    Status

    Triage Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions