go: go/types: add Object.Origin [freeze exception]

I propose to add the following methods to types.Var and types.Func:

// Origin returns the canonical object for its receiver, i.e. the object
// recorded in Info.Defs.
//
// For instantiated variables Origin returns the type-parameterized object from
// which the receiver was created. For all other variables Origin returns the
// receiver.
func (obj *Var) Origin() *Var 

// Origin returns the canonical object for its receiver, i.e. the object
// recorded in Info.Defs.
//
// For instantiated functions Origin returns the type-parameterized object from
// which the receiver was created. For all other functions, Origin returns
// receiver.
func (obj *Func) Origin() *Func

While instantiating types, we necessarily have to create instantiated copies of their methods/fields/parameters/etc. However, as we work on updating tools for generics, an emerging theme is that is useful to be able to answer the following two questions:

  1. Is this object canonical?
  2. What is the canonical object corresponding to this object?

Right now, these are not easy questions to answer. For methods, these can be answered by a lookup on the receiver base origin type. For fields / parameters / etc., it is more difficult and may require finding the relevant *ast.Ident and looking in Info.Defs.

Adding an Origin method to these types answers both of these questions trivially, at the cost of a pointer per object. If the added memory is a concern, there are several internal fields on types.object used during type-checking that may be externalized to free memory.

CC @griesemer @dominikh @timothy-king @mdempsky

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 3
  • Comments: 20 (19 by maintainers)

Most upvoted comments

This freeze exemption has been approved. Nothing else is required for the exemption request.

CC @golang/release

This issue was filed early in the dev cycle, but due to ongoing discussion and the abbreviated cycle, was not accepted until a few days before the freeze. With other priorities in go/types (particularly the urgent https://go.dev/issue/52715), I didn’t get to the implementation of this feature before the freeze.

I’d like to request a freeze exception to land this proposal for 1.19, as it allows us to redefine a ‘canonical’ invariant that was broken with generics. Certain types of static analysis (such as https://github.com/dominikh/go-tools/issues/1215 or some Google-internal analyses) would be facilitated by these new APIs.

Please let me know if you’d like me to file a new issue for the freeze exception request, or if you need any other information to help make a determination.

Discussed this with @griesemer, and it seems like the arguments for concrete result types are compelling:

  • Fewer type assertions at the call site.
  • Less likely to introduce a typed nil.

On the other hand, we wouldn’t be able to pass around interface{ Origin() Object } as @dominikh suggests, but it would of course be easy to write a helper func originObject(Object) Object.

By way of analogy, we document that Type() of a *Func is always *Signature, yet I have seen many places in x/tools where we use a commaok type assertion and handle the case that the Type() of a *Func is not a *Signature. I would guess that returning Object will lead to similar misuse.