Skip to content

go/types: API changes to support explicit Alias nodes #63223

@griesemer

Description

@griesemer

Background

Currently, when a type alias declaration

type A = some_type

is type-checked, go/types creates a TypeName Object for the type alias A but the type of the TypeName is the type of some_type. When A is used as identifier in source code, the denoted type is some_type, which if it's a defined type will have a different name than A; and if it's not a defined type, it will be a type literal, which is also not called A. As a consequence, an error message related to the type A will not report A but some_type or whatever type A is a alias for. This is a long-standing issue that we'd like to fix.

Furthermore, with proposal #46477 accepted (parameterized type aliases), currently go/types doesn't have a way to represent a parameterized alias type.

Both these problems can be addressed with an explicit Alias type node. This proposal is about introducing such a new type node.

Proposal

We introduce a new go/types node called Alias:

// An Alias represents an alias type.
type Alias struct {
	obj     *TypeName // corresponding declared alias object
	fromRHS Type      // RHS of type alias declaration; may be an alias
	actual  Type      // actual (aliased) type; never an alias

	// additional fields for type parameters
}

// Obj returns the type name for the declaration defining the alias a.
func (a *Alias) Obj() *TypeName { return a.obj }

// Methods supported by all types
func (*Alias) Underlying() Type
func (*Alias) String() string 

An Alias node stands for a type alias. An Alias node is created with a factory function:

func NewAlias(obj *TypeName, rhs Type) *Alias

When an Alias node is encountered, go/types and related tool clients may need to indirect to the actual type the Alias node is a type alias for. This can be done through a new function

// Unalias returns t if it is not an alias type;
// otherwise it follows t's alias chain until it
// reaches a non-alias type which is then returned.
// Consequently, the result is never an alias type.
func Unalias(t Type) Type

Tools that care about the actual un-aliased type will need to call Unalias everywhere in the tools code where there is a type assertion or type switch and the type may be an alias type.

This is a non-backward compatible change to go/types and it may require pervasive changes to tools, with a potentially long bug tail. For that reason we also propose to introduce a new types.Config flag:

// If EnableAlias is set, alias declarations produce an Alias type.
// Otherwise the alias information is only in the type name, which
// points directly to the actual (aliased) type.
EnableAlias bool

Unless EnableAlias is set, go/types behaves exactly as before. If EnableAlias is set, Alias nodes appear for type alias declarations, and clients of go/types will need to be aware of that.

Discussion

This proposal only introduces the Alias node functionality, without support for type parameters. We believe this is a significant change which should be done separately from support for parameterized alias types, ideally for Go 1.22. Once the new API is settled and clients have adjusted, we would then extend the Alias node with support for type parameters and instantiation, presumably for Go 1.23. As that step doesn't introduce a new type node, we expect it to be easier to adjust clients.

Unfortunately we don't see a way around introducing an explicit Alias node if we want to support parameterization, and therefore we don't see a way to support parameterized alias types in a backward-compatible way. The EnableAlias configuration flag should help ease the transition: tools will continue to work as before if this proposal is accepted. Only when they set the configuration flag will they need to be aware of the new type node.

Implementation

An initial implementation can be found here: CL 521956 with unexported API changes.
The compiler doesn't set EnableAlias yet. Also, changes to the compiler (calls to Unalias) and exporters/importers are missing.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions