Skip to content

Commit d7b0ac1

Browse files
committed
gotypes tutorial: update for Aliases
This CL updates the tutorial to mention (materialized) aliases and the upcoming parameterized aliases of go1.24. We don't yet attempt a thorough treatment of generics. It also corrects a number of other obsolete or inaccurate statements. Change-Id: I2bbdc1a9000eb5bd214620c511d7e60692b4d9de Reviewed-on: https://go-review.googlesource.com/c/example/+/620075 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 1a5e218 commit d7b0ac1

File tree

2 files changed

+309
-140
lines changed

2 files changed

+309
-140
lines changed

gotypes/README.md

+156-70
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ This document is maintained by Alan Donovan `[email protected]`.
2222
1. [Struct Types](#struct-types)
2323
1. [Tuple Types](#tuple-types)
2424
1. [Function and Method Types](#function-and-method-types)
25+
1. [Alias Types](#alias-types)
2526
1. [Named Types](#named-types)
2627
1. [Interface Types](#interface-types)
28+
1. [TypeParam types](#typeparam-types)
29+
1. [Union types](#union-types)
2730
1. [TypeAndValue](#typeandvalue)
2831
1. [Selections](#selections)
2932
1. [Ids](#ids)
@@ -103,9 +106,6 @@ type, or has an inappropriate type for its context; this is known as
103106
_type deduction_.
104107
Third, for every constant expression in the program, it determines the
105108
value of that constant; this is known as _constant evaluation_.
106-
107-
108-
109109
Superficially, it appears that these three processes could be done
110110
sequentially, in the order above, but perhaps surprisingly, they must
111111
be done together.
@@ -115,9 +115,6 @@ Conversely, the type of an expression may depend on the value of a
115115
constant, since array types contain constants.
116116
As a result, type deduction and constant evaluation must be done
117117
together.
118-
119-
120-
121118
As another example, we cannot resolve the identifier `k` in the composite
122119
literal `T{k: 0}` until we know whether `T` is a struct type.
123120
If it is, then `k` must be found among `T`'s fields.
@@ -264,7 +261,7 @@ Scope: package "cmd/hello" scope 0x820533590 {
264261

265262
A package's `Path`, such as `"encoding/json"`, is the string
266263
by which import declarations identify it.
267-
It is unique within a `$GOPATH` workspace,
264+
It is unique within a workspace,
268265
and for published packages it must be globally unique.
269266

270267

@@ -282,7 +279,7 @@ which provides access to all the named entities or
282279
[_objects_](#objects) declared at package level.
283280
`Imports` returns the set of packages directly imported by this
284281
one, and may be useful for computing dependencies
285-
([Initialization Order](#initialization-order)).
282+
(see [Initialization Order](#initialization-order)).
286283

287284

288285

@@ -336,12 +333,8 @@ offset, though usually we just call its `String` method:
336333
fmt.Println(fset.Position(obj.Pos())) // "hello.go:10:6"
337334

338335

339-
Not all objects carry position information.
340-
Since the file format for compiler export data ([Imports](#imports))
341-
does not record position information, calling `Pos` on an object
342-
imported from such a file returns zero, also known as
343-
`token.NoPos`.
344-
336+
Objects for predeclared functions and types such as `len` and `int`
337+
do not have a valid (non-zero) position: `!obj.Pos().IsValid()`.
345338

346339

347340
There are eight kinds of objects in the Go type checker.
@@ -367,14 +360,22 @@ possible types, and we commonly use a type switch to distinguish them.
367360
| *Nil // predeclared nil
368361

369362

370-
`Object`s are canonical.
371-
That is, two `Object`s `x` and `y` denote the same
372-
entity if and only if `x==y`.
363+
364+
Objects are canonical.
365+
That is, two Objects `x` and `y` denote the same entity if and only if `x==y`.
366+
(This is generally true but beware that parameterized types complicate matters; see
367+
https://github.com/golang/exp/tree/master/typeparams/example for details.)
368+
373369
Object identity is significant, and objects are routinely compared by
374370
the addresses of the underlying pointers.
375-
Although a package-level object is uniquely identified by its name
376-
and enclosing package, for other objects there is no simple way to
377-
obtain a string that uniquely identifies it.
371+
A package-level object (func/var/const/type) can be uniquely
372+
identified by its name and enclosing package.
373+
The [`golang.org/x/tools/go/types/objectpath`](https://pkg.go.dev/golang.org/x/tools/go/types/objectpath)
374+
package defines a naming scheme for objects that are
375+
[exported](#imports) from their package or are unexported but form part of the
376+
type of an exported object.
377+
But for most objects, including all function-local objects,
378+
there is no simple way to obtain a string that uniquely identifies it.
378379

379380

380381

@@ -409,18 +410,20 @@ And some kinds of objects have methods in addition to those required by the
409410

410411

411412
`(*Func).Scope` returns the [lexical block](#scopes)
412-
containing the function's parameters, results,
413+
containing the function's type parameters, parameters, results,
413414
and other local declarations.
414415
`(*Var).IsField` distinguishes struct fields from ordinary
415416
variables, and `(*Var).Anonymous` discriminates named fields like
416417
the one in `struct{T T}` from anonymous fields like the one in `struct{T}`.
417418
`(*Const).Val` returns the value of a named [constant](#constants).
418419

419420

420-
`(*TypeName).IsAlias`, introduced in Go 1.9, reports whether the
421-
type name is simply an alias for a type (as in `type I = int`),
422-
as opposed to a definition of a [`Named`](#named-types) type, as
423-
in `type Celsius float64`.
421+
`(*TypeName).IsAlias` reports whether the type name declares an alias
422+
for an existing type (as in `type I = int`), as opposed to defining a new
423+
[`Named`](#named-types) type, as in `type Celsius float64`.
424+
(Most `TypeName`s for which `IsAlias()` is true have a `Type()` of
425+
type `*types.Alias`, but `IsAlias()` is also true for the predeclared
426+
`byte` and `rune` types, which are aliases for `uint8` and `int32`.)
424427

425428

426429
`(*PkgName).Imported` returns the package (for instance,
@@ -436,9 +439,9 @@ We'll look more closely at this in [Imports](#imports).
436439
All relationships between the syntax trees (`ast.Node`s) and type
437440
checker data structures such as `Object`s and `Type`s are
438441
stored in mappings outside the syntax tree itself.
439-
Be aware that the `go/ast` package also defines a type called
440-
`Object` that resembles---and predates---the type checker's
441-
`Object`, and that `ast.Object`s are held directly by
442+
Be aware that the `go/ast` package also defines an older deprecated
443+
type called `Object` that resembles---and predates---the type
444+
checker's `Object`, and that `ast.Object`s are held directly by
442445
identifiers in the AST.
443446
They are created by the parser, which has a necessarily limited view
444447
of the package, so the information they represent is at best partial and
@@ -974,7 +977,7 @@ Here is the interface:
974977
}
975978

976979

977-
And here are the eleven concrete types that satisfy it:
980+
And here are the 14 concrete types that satisfy it:
978981

979982

980983
Type = *Basic
@@ -986,12 +989,17 @@ And here are the eleven concrete types that satisfy it:
986989
| *Struct
987990
| *Tuple
988991
| *Signature
992+
| *Alias
989993
| *Named
990994
| *Interface
995+
| *Union
996+
| *TypeParam
991997

992998

993999
With the exception of `Named` types, instances of `Type` are
9941000
not canonical.
1001+
(Even for `Named` types, parameterized types complicate matters; see
1002+
https://github.com/golang/exp/tree/master/typeparams/example.)
9951003
That is, it is usually a mistake to compare types using `t1==t2`
9961004
since this equivalence is not the same as the
9971005
[type identity relation](https://golang.org/ref/spec#Type_identity)
@@ -1052,8 +1060,8 @@ basic type this is.
10521060
The kinds `Bool`, `String`, `Int16`, and so on,
10531061
represent the corresponding predeclared boolean, string, or numeric
10541062
types.
1055-
There are two synonyms: `Byte` is equivalent to `Uint8`
1056-
and `Rune` is equivalent to `Int32`.
1063+
There are two aliases: `Byte` is an alias for `Uint8`
1064+
and `Rune` is an alias for `Int32`.
10571065
The kind `UnsafePointer` represents `unsafe.Pointer`.
10581066
The kinds `UntypedBool`, `UntypedInt` and so on represent
10591067
the six kinds of "untyped" constant types: boolean, integer, rune,
@@ -1082,21 +1090,20 @@ modify it.
10821090

10831091

10841092
A few minor subtleties:
1085-
According to the Go spec, pre-declared types such as `int` are
1086-
named types for the purposes of assignability, even though the type
1087-
checker does not represent them using `Named`.
1088-
And `unsafe.Pointer` is a pointer type for the purpose of
1089-
determining whether the receiver type of a method is legal, even
1090-
though the type checker does not represent it using `Pointer`.
1091-
10921093

1094+
- According to the Go spec, pre-declared types such as `int` are
1095+
named types for the purposes of assignability, even though the type
1096+
checker does not represent them using `Named`.
10931097

1094-
The "untyped" types are usually only ascribed to constant expressions,
1095-
but there is one exception.
1096-
A comparison `x==y` has type "untyped bool", so the result of
1097-
this expression may be assigned to a variable of type `bool` or
1098-
any other named boolean type.
1098+
- `unsafe.Pointer` is a pointer type for the purpose of
1099+
determining whether the receiver type of a method is legal, even
1100+
though the type checker does not represent it using `Pointer`.
10991101

1102+
- The "untyped" types are usually only ascribed to constant expressions,
1103+
but there is one exception.
1104+
A comparison `x==y` has type "untyped bool", so the result of
1105+
this expression may be assigned to a variable of type `bool` or
1106+
any other named boolean type.
11001107

11011108

11021109
## Simple Composite Types
@@ -1262,21 +1269,73 @@ These types are recorded during type checking for later use
12621269

12631270

12641271

1265-
## Named Types
1272+
## Alias Types
1273+
1274+
Type declarations come in two forms, aliases and defined types.
12661275

1276+
Aliases, though introduced only in Go 1.9 and not very common, are
1277+
simplest, so we'll present them first and explain defined types in
1278+
the next section ("Named Types").
12671279

1268-
Type declarations come in two forms.
1269-
The simplest kind, introduced in Go 1.9,
1270-
merely declares a (possibly alternative) name for an existing type.
1271-
Type names used in this way are informally called _type aliases_.
1272-
For example, this declaration lets you use the type
1273-
`Dictionary` as an alias for `map[string]string`:
1280+
An alias type declaration declares an alternative name for an existing
1281+
type. For example, this declaration lets you use the type `Dictionary`
1282+
as a synonym for `map[string]string`:
12741283

12751284
type Dictionary = map[string]string
12761285

1277-
The declaration creates a `TypeName` object for `Dictionary`. The
1278-
object's `IsAlias` method returns true, and its `Type` method returns
1279-
a `Map` type that represents `map[string]string`.
1286+
The declaration creates a `TypeName` object for `Dictionary`.
1287+
The object's `IsAlias` method returns true,
1288+
and its `Type` method returns an `Alias`:
1289+
1290+
type Alias struct{ ... }
1291+
func (a *Alias) Obj() *TypeName
1292+
func (a *Alias) Origin() *Alias
1293+
func (a *Alias) Rhs() Type
1294+
func (a *Alias) SetTypeParams(tparams []*TypeParam)
1295+
func (a *Alias) TypeArgs() *TypeList
1296+
func (a *Alias) TypeParams() *TypeParamList
1297+
1298+
The type on the right-hand side of an alias declaration,
1299+
such as `map[string]string` in the example above,
1300+
can be accessed using the `Rhs()` method.
1301+
The `types.Unalias(t)` helper function recursively applies `Rhs`,
1302+
removing all `Alias` types from the operand t and returning the
1303+
outermost non-alias type.
1304+
1305+
The `Obj` method returns the declaring `TypeName` object, such as
1306+
`Dictionary`; it provides the name, position, and other properties of
1307+
the declaration. Conversely, the `TypeName` object's `Type` method
1308+
returns the `Alias` type.
1309+
1310+
Starting with Go 1.24, alias types may have type parameters.
1311+
For example, this declaration creates an Alias type with
1312+
a type parameter:
1313+
1314+
type Set[T comparable] = map[T]bool
1315+
1316+
Each instantiation such as `Set[string]` is identical to the
1317+
corresponding instantiation of the alias' right-hand side type, such
1318+
as `map[string]bool`.
1319+
1320+
The remaining methods--Origin, SetTypeParams, TypeArgs,
1321+
TypeParams--are all concerned with type parameters. For now, see
1322+
https://github.com/golang/exp/tree/master/typeparams/example.
1323+
1324+
Prior to Go 1.22, aliases were not materialized as `Alias` types:
1325+
each reference to an alias type such as `Dictionary` would be
1326+
immediately replaced by its right-hand side type, leaving no
1327+
indication in the output of the type checker that an alias was
1328+
present.
1329+
By materializing alias types, optionally in Go 1.22 and by default
1330+
starting in Go 1.23, we can more faithfully record the structure of
1331+
the program, which improves the quality of diagnostic messages and
1332+
enables certain analyses and code transformations. And, crucially, it
1333+
enabled the addition of parameterized aliases in Go 1.24.)
1334+
1335+
1336+
1337+
## Named Types
1338+
12801339

12811340

12821341
The second form of type declaration, and the only kind prior to Go
@@ -1292,10 +1351,11 @@ from any other type, including `float64`. The declaration binds the
12921351

12931352
Since Go 1.9, the Go language specification has used the term _defined
12941353
types_ instead of named types;
1295-
the essential property of a defined type is not that it has a name,
1354+
the essential property of a defined type is not that it has a name
1355+
(aliases and type parameters also have names)
12961356
but that it is a distinct type with its own method set.
12971357
However, the type checker API predates that
1298-
change and instead calls defined types "named" types.
1358+
change and instead calls defined types `Named` types.
12991359

13001360
type Named struct{ ... }
13011361
func (*Named) NumMethods() int
@@ -1321,8 +1381,8 @@ methods than this list. We'll return to this in [Method Sets](#method-sets).
13211381

13221382

13231383
Every `Type` has an `Underlying` method, but for all of them
1324-
except `*Named`, it is simply the identity function.
1325-
For a named type, `Underlying` returns its underlying type, which
1384+
except `*Named` and `*Alias`, it is simply the identity function.
1385+
For a named or alias type, `Underlying` returns its underlying type, which
13261386
is always an unnamed type.
13271387
Thus `Underlying` returns `int` for both `T` and
13281388
`U` below.
@@ -1335,14 +1395,13 @@ Thus `Underlying` returns `int` for both `T` and
13351395
Clients of the type checker often use type assertions or type switches
13361396
with a `Type` operand.
13371397
When doing so, it is often necessary to switch on the type that
1338-
_underlies_ the type of interest, and failure to do so may be a
1339-
bug.
1398+
underlies the type of interest, and failure to do so may be a bug.
13401399

13411400
This is a common pattern:
13421401

13431402

13441403
// handle types of composite literal
1345-
switch u := t.Underlying().(type) {
1404+
switch u := t.Underlying().(type) { // remove any *Named and *Alias types
13461405
case *Struct: // ...
13471406
case *Map: // ...
13481407
case *Array, *Slice: // ...
@@ -1423,6 +1482,36 @@ interface `v`, then the type assertion is not legal, as in this example:
14231482

14241483

14251484

1485+
1486+
## TypeParam types
1487+
1488+
1489+
A `TypeParam` is the type of a type parameter.
1490+
For example, the type of the variable `x` in the `identity` function
1491+
below is a `TypeParam`:
1492+
1493+
func identity[T any](x T) T { return x }
1494+
1495+
As with `Alias` and `Named` types, each `TypeParam` has an associated
1496+
`TypeName` object that provides its name, position, and other
1497+
properties of the declaration.
1498+
1499+
See https://github.com/golang/exp/tree/master/typeparams/example
1500+
for a more thorough treatment of parameterized types.
1501+
1502+
1503+
1504+
1505+
## Union types
1506+
1507+
A `Union` is the type of type-parameter constraint of the form `func
1508+
f[T int | string]`.
1509+
1510+
See https://github.com/golang/exp/tree/master/typeparams/example
1511+
for a more thorough treatment of parameterized types.
1512+
1513+
1514+
14261515
## TypeAndValue
14271516

14281517

@@ -2264,10 +2353,9 @@ compiler file formats, and so on.
22642353
}
22652354

22662355

2267-
Most of our examples used the simplest `Importer` implementation,
2356+
Most of our examples used the trivial `Importer` implementation,
22682357
`importer.Default()`, provided by the `go/importer` package.
2269-
This importer looks in `$GOROOT` and `$GOPATH` for `.a`
2270-
files written by the compiler (`gc` or `gccgo`)
2358+
This importer looks for `.a` files written by the compiler
22712359
that was used to build the program.
22722360
In addition to object code, these files contain _export data_,
22732361
that is, a description of all the objects declared by the package, and
@@ -2279,13 +2367,11 @@ transitive dependency.
22792367

22802368

22812369
Compiler export data is compact and efficient to locate, load, and
2282-
parse, but it has several shortcomings.
2283-
First, it does not contain position information for imported
2284-
objects, reducing the quality of certain diagnostic messages.
2285-
Second, it does not contain complete syntax trees nor semantic information
2286-
about the contents of function bodies, so it is not suitable for
2287-
interprocedural analyses.
2288-
Third, compiler object data may be stale. Nothing detects or ensures
2370+
parse, but it has some shortcomings.
2371+
First, it does not contain complete syntax trees nor semantic
2372+
information about the bodies of all functions, so it is not
2373+
suitable for interprocedural analyses.
2374+
Second, compiler object data may be stale. Nothing detects or ensures
22892375
that the object files are more recent than the source files from which
22902376
they were derived.
22912377
Generally, object data for standard packages is likely to be

0 commit comments

Comments
 (0)