1
0
mirror of https://github.com/golang/go synced 2024-11-22 05:54:40 -07:00

- language to define type equality rigorously

- language for type guards
- fixed language for break statements

Also: Removed uses of "we" and replaced by impersonal language.
Minor cosmetic changes.

DELTA=237  (160 added, 34 deleted, 43 changed)
OCL=18620
CL=18800
This commit is contained in:
Robert Griesemer 2008-11-07 13:34:37 -08:00
parent f8d7f5bd81
commit 434c6052d8

View File

@ -4,7 +4,7 @@ The Go Programming Language Specification (DRAFT)
Robert Griesemer, Rob Pike, Ken Thompson Robert Griesemer, Rob Pike, Ken Thompson
---- ----
(November 4, 2008) (November 7, 2008)
This document is a semi-formal specification of the Go systems This document is a semi-formal specification of the Go systems
@ -157,6 +157,7 @@ Contents
Channel types Channel types
Function types Function types
Interface types Interface types
Type equality
Expressions Expressions
Operands Operands
@ -289,15 +290,15 @@ implementation, Go treats these as distinct characters.
Characters Characters
---- ----
In the grammar we use the notation In the grammar the term
utf8_char utf8_char
to refer to an arbitrary Unicode code point encoded in UTF-8. We use denotes an arbitrary Unicode code point encoded in UTF-8. Similarly,
non_ascii non_ascii
to refer to the subset of "utf8_char" code points with values >= 128. denotes the subset of "utf8_char" code points with values >= 128.
Letters and digits Letters and digits
@ -719,6 +720,7 @@ Type declarations
---- ----
A type declaration specifies a new type and binds an identifier to it. A type declaration specifies a new type and binds an identifier to it.
The identifier is called the ``type name''; it denotes the type.
TypeDecl = "type" Decl<TypeSpec> . TypeDecl = "type" Decl<TypeSpec> .
TypeSpec = identifier Type . TypeSpec = identifier Type .
@ -834,17 +836,23 @@ TODO: export as a mechanism for public and private struct fields?
Types Types
---- ----
A type specifies the set of values that variables of that type may A type specifies the set of values that variables of that type may assume
assume, and the operators that are applicable. and the operators that are applicable.
There are basic types and composite types. Basic types are predeclared. A type may be specified by a type name (§Type declarations)
Composite types are arrays, maps, channels, structures, functions, pointers, or a type literal.
and interfaces. They are constructed from other (basic or composite) types.
Type = Type = TypeName | TypeLit .
TypeName | ArrayType | ChannelType | InterfaceType |
FunctionType | MapType | StructType | PointerType .
TypeName = QualifiedIdent. TypeName = QualifiedIdent.
TypeLit =
ArrayType | StructType | PointerType | FunctionType |
ChannelType | MapType | InterfaceType .
There are basic types and composite types. Basic types are predeclared and
denoted by their type names.
Composite types are arrays, maps, channels, structures, functions, pointers,
and interfaces. They are constructed from other (basic or composite) types
and denoted by their type names or by type literals.
Types may be ``complete'' or ''incomplete''. Basic, pointer, function and Types may be ``complete'' or ''incomplete''. Basic, pointer, function and
interface types are always complete (although their components, such interface types are always complete (although their components, such
@ -868,7 +876,7 @@ Need to resolve.
The ``static type'' (or simply ``type'') of a variable is the type defined by The ``static type'' (or simply ``type'') of a variable is the type defined by
the variable's declaration. The ``dynamic type'' of a variable is the actual the variable's declaration. The ``dynamic type'' of a variable is the actual
type of the value stored in a variable at runtime. Except for variables of type of the value stored in a variable at run-time. Except for variables of
interface type, the dynamic type of a variable is always its static type. interface type, the dynamic type of a variable is always its static type.
Variables of interface type may hold values with different dynamic types Variables of interface type may hold values with different dynamic types
@ -978,10 +986,6 @@ If the length is present in the declaration, the array is called
ArrayLength = Expression . ArrayLength = Expression .
ElementType = CompleteType . ElementType = CompleteType .
Type equality: Two array types are equal only if both have the same element
type and if both are either fixed arrays with the same array length, or both
are open arrays.
The length of an array "a" can be discovered using the built-in function The length of an array "a" can be discovered using the built-in function
len(a) len(a)
@ -1169,11 +1173,6 @@ This allows the construction of mutually recursive types such as:
type S1 struct { s2 *S2 } type S1 struct { s2 *S2 }
type S2 struct { s1 *S1 } type S2 struct { s1 *S1 }
Type equality: Two struct types are equal only if both have the same number
of fields in the same order, corresponding fields are either both named or
anonymous, and the corresponding field types are equal. Specifically,
field names don't have to match.
Assignment compatibility: Structs are assignment compatible to variables of Assignment compatibility: Structs are assignment compatible to variables of
equal type only. equal type only.
@ -1201,9 +1200,6 @@ such as:
type S1 struct { s2 *S2 } type S1 struct { s2 *S2 }
type S2 struct { s1 *S1 } type S2 struct { s1 *S1 }
Type equality: Two pointer types are equal only if both have equal
base types.
Assignment compatibility: A pointer is assignment compatible to a variable Assignment compatibility: A pointer is assignment compatible to a variable
of pointer type, only if both types are equal. of pointer type, only if both types are equal.
@ -1235,9 +1231,6 @@ Allocation: A map may only be used as a base type of a pointer type.
There are no variables, parameters, array, struct, or map fields of There are no variables, parameters, array, struct, or map fields of
map type, only of pointers to maps. map type, only of pointers to maps.
Type equivalence: Two map types are equal only if both have equal
key and value types.
Assignment compatibility: A pointer to a map type is assignment Assignment compatibility: A pointer to a map type is assignment
compatible to a variable of pointer to map type only if both types compatible to a variable of pointer to map type only if both types
are equal. are equal.
@ -1251,17 +1244,18 @@ to synchronize execution and exchange values of a specified type. This
type must be a complete type (§Types). type must be a complete type (§Types).
Upon creation, a channel can be used both to send and to receive. Upon creation, a channel can be used both to send and to receive.
By conversion or assignment, a 'full' channel may be constrained only to send or By conversion or assignment, a channel may be constrained only to send or
to receive. Such a restricted channel is called a 'send channel' or a 'receive channel'. to receive. This constraint is called a channel's ``direction''; either
bi-directional (unconstrained), send, or receive.
ChannelType = FullChannel | SendChannel | RecvChannel . ChannelType = Channel | SendChannel | RecvChannel .
FullChannel = "chan" ValueType . Channel = "chan" ValueType .
SendChannel = "chan" "<-" ValueType . SendChannel = "chan" "<-" ValueType .
RecvChannel = "<-" "chan" ValueType . RecvChannel = "<-" "chan" ValueType .
chan T // a channel that can exchange values of type T chan T // can send and receive values of type T
chan <- float // a channel that can only be used to send floats chan <- float // can only be used to send floats
<-chan int // a channel that can receive only ints <-chan int // can receive only ints
Channel variables always have type pointer to channel. Channel variables always have type pointer to channel.
It is an error to attempt to use a channel value and in It is an error to attempt to use a channel value and in
@ -1311,11 +1305,6 @@ In particular, v := func() {} creates a variable of type *(). To call the
function referenced by v, one writes v(). It is illegal to dereference a function referenced by v, one writes v(). It is illegal to dereference a
function pointer. function pointer.
Type equality: Two function types are equal if both have the same number
of parameters and result values and if corresponding parameter and result
types are equal. In particular, the parameter and result names are ignored
for the purpose of type equivalence.
Assignment compatibility: A function pointer can be assigned to a function Assignment compatibility: A function pointer can be assigned to a function
(pointer) variable only if both function types are equal. (pointer) variable only if both function types are equal.
@ -1337,9 +1326,9 @@ the set of methods specified by the interface type, and the value "nil".
Close(); Close();
} }
Any type whose interface has, possibly as a subset, the complete Any type (including interface types) whose interface has, possibly as a
set of methods of an interface I is said to implement interface I. subset, the complete set of methods of an interface I is said to implement
For instance, if two types S1 and S2 have the methods interface I. For instance, if two types S1 and S2 have the methods
func (p T) Read(b Buffer) bool { return ... } func (p T) Read(b Buffer) bool { return ... }
func (p T) Write(b Buffer) bool { return ... } func (p T) Write(b Buffer) bool { return ... }
@ -1354,14 +1343,14 @@ All types implement the empty interface:
interface {} interface {}
In general, a type implements an arbitrary number of interfaces. In general, a type implements an arbitrary number of interfaces.
For instance, if we have For instance, consider the interface
type Lock interface { type Lock interface {
lock(); lock();
unlock(); unlock();
} }
and S1 and S2 also implement If S1 and S2 also implement
func (p T) lock() { ... } func (p T) lock() { ... }
func (p T) unlock() { ... } func (p T) unlock() { ... }
@ -1381,14 +1370,120 @@ This allows the construction of mutually recursive types such as:
bar(T1) int; bar(T1) int;
} }
Type equivalence: Two interface types are equal only if both declare the same
number of methods with the same names, and corresponding (by name) methods
have the same function types.
Assignment compatibility: A value can be assigned to an interface variable Assignment compatibility: A value can be assigned to an interface variable
if the static type of the value implements the interface or if the value is "nil". if the static type of the value implements the interface or if the value is "nil".
Type equality
----
Types may be ``different'', ``structurally equal'', or ``identical''.
Go is a type-safe language; generally different types cannot be mixed
in binary operations, and values cannot be assigned to variables of different
types. However, values may be assigned to variables of structually
equal types. Finally, type guards succeed only if the dynamic type
is identical to or implements the type tested against (§Type guards).
Structural type equality (equality for short) is defined by these rules:
Two type names denote equal types if the types in the corresponding declarations
are equal. Two type literals specify equal types if they have the same
literal structure and corresponding components have equal types. Loosely
speaking, two types are equal if their values have the same layout in memory.
More precisely:
- Two array types are equal if they have equal element types and if they
are either fixed arrays with the same array length, or they are open
arrays.
- Two struct types are equal if they have the same number of fields in the
same order, corresponding fields are either both named or both anonymous,
and corresponding field types are equal. Note that field names
do not have to match.
- Two pointer types are equal if they have equal base types.
- Two function types are equal if they have the same number of parameters
and result values and if corresponding parameter and result types are
equal (a "..." parameter is equal to another "..." parameter).
Note that parameter and result names do not have to match.
- Two channel types are equal if they have equal value types and
the same direction.
- Two map types are equal if they have equal key and value types.
- Two interface types are equal if they have the same set of methods
with the same names and equal function types. Note that the order
of the methods in the respective type declarations is irrelevant.
Type identity is defined by these rules:
Two type names denote identical types if they originate in the same
type declaration. Two type literals specify identical types if they have the
same literal structure and corresponding components have identical types.
More precisely:
- Two array types are identical if they have identical element types and if
they are either fixed arrays with the same array length, or they are open
arrays.
- Two struct types are identical if they have the same number of fields in
the same order, corresponding fields either have both the same name or
are both anonymous, and corresponding field types are identical.
- Two pointer types are identical if they have identical base types.
- Two function types are identical if they have the same number of
parameters and result values both with the same (or absent) names, and
if corresponding parameter and result types are identical (a "..."
parameter is identical to another "..." parameter with the same name).
- Two channel types are identical if they have identical value types and
the same direction.
- Two map types are identical if they have identical key and value types.
- Two interface types are identical if they have the same set of methods
with the same names and identical function types. Note that the order
of the methods in the respective type declarations is irrelevant.
Note that the type denoted by a type name is identical only to the type literal
in the type name's declaration.
Finally, two types are different if they are not structurally equal.
(By definition, they cannot be identical, either).
For instance, given the declarations
type (
T0 []string;
T1 []string
T2 struct { a, b int };
T3 struct { a, c int };
T4 *(int, float) *T0
T5 *(x int, y float) *[]string
)
these are some types that are equal
T0 and T0
T0 and []string
T2 and T3
T4 and T5
T3 and struct { a int; int }
and these are some types that are identical
T0 and T0
[]int and []int
struct { a, b *T5 } and struct { a, b *T5 }
As an example, "T0" and "T1" are equal but not identical because they have
different declarations.
Expressions Expressions
---- ----
@ -1511,7 +1606,7 @@ Given
type Rat struct { num, den int }; type Rat struct { num, den int };
type Num struct { r Rat; f float; s string }; type Num struct { r Rat; f float; s string };
we can write one can write
pi := Num{Rat{22, 7}, 3.14159, "pi"}; pi := Num{Rat{22, 7}, 3.14159, "pi"};
@ -1576,7 +1671,7 @@ Primary expressions
Selector = "." identifier . Selector = "." identifier .
Index = "[" Expression "]" . Index = "[" Expression "]" .
Slice = "[" Expression ":" Expression "]" . Slice = "[" Expression ":" Expression "]" .
TypeGuard = "." "(" QualifiedIdent ")" . TypeGuard = "." "(" Type ")" .
Call = "(" [ ExpressionList ] ")" . Call = "(" [ ExpressionList ] ")" .
@ -1660,7 +1755,7 @@ declarations:
var p *T2; // with p != nil and p.T1 != nil var p *T2; // with p != nil and p.T1 != nil
we can write: one can write:
p.z // (*p).z p.z // (*p).z
p.y // ((*p).T1).y p.y // ((*p).T1).y
@ -1734,7 +1829,41 @@ would have no effect on ``a''.
Type guards Type guards
---- ----
TODO: write this section For an expression "x" and a type "T", the primary expression
x.(T)
asserts that the value stored in "x" is an element of type "T" (§Types).
The notation ".(T)" is called a ``type guard'', and "x.(T)" is called
a ``guarded expression''. The type of "x" must be an interface type.
More precisely, if "T" is not an interface type, the expression asserts
that the dynamic type of "x" is identical to the type "T" (§Types).
If "T" is an interface type, the expression asserts that the dynamic type
of T implements the interface "T" (§Interface types). Because it can be
verified statically, a type guard in which the static type of "x" implements
the interface "T" is illegal. The type guard is said to succeed if the
assertion holds.
If the type guard succeeds, the value of the guarded expression is the value
stored in "x" and its type is "T". If the type guard fails, a run-time
exception occurs. In other words, even though the dynamic type of "x"
is only known at run-time, the type of the guarded expression "x.(T)" is
known to be "T" in a correct program.
As a special form, if a guarded expression is used in an assignment
v, ok = x.(T)
v, ok := x.(T)
the result of the guarded expression is a pair of values with types "(T, bool)".
If the type guard succeeds, the expression returns the pair "(x.(T), true)";
that is, the value stored in "x" (of type "T") is assigned to "v", and "ok"
is set to true. If the type guard fails, the value in "v" is set to the initial
value for the type of "v" (§Program initialization and execution), and "ok" is
set to false. No run-time exception occurs in this case.
TODO add examples
Calls Calls
@ -1773,7 +1902,7 @@ TODO expand this section (right now only "..." parameters are covered).
Inside a function, the type of the "..." parameter is the empty interface Inside a function, the type of the "..." parameter is the empty interface
"interface {}". The dynamic type of the parameter - that is, the type of "interface {}". The dynamic type of the parameter - that is, the type of
the actual value stored in the parameter - is of the form (in pseudo- the value stored in the parameter - is of the form (in pseudo-
notation) notation)
*struct { *struct {
@ -1791,7 +1920,7 @@ Thus, arguments provided in place of a "..." parameter are wrapped into
a corresponding struct, and a pointer to the struct is passed to the a corresponding struct, and a pointer to the struct is passed to the
function instead of the actual arguments. function instead of the actual arguments.
For instance, given the function For instance, consider the function
func f(x int, s string, f_extra ...) func f(x int, s string, f_extra ...)
@ -1802,7 +1931,7 @@ and the call
Upon invocation, the parameters "3.14", "true", and "*[3]int{1, 2, 3}" Upon invocation, the parameters "3.14", "true", and "*[3]int{1, 2, 3}"
are wrapped into a struct and the pointer to the struct is passed to f. are wrapped into a struct and the pointer to the struct is passed to f.
In f the type of parameter "f_extra" is "interface{}". In f the type of parameter "f_extra" is "interface{}".
The dynamic type of "f_extra" is the type of the actual value assigned The dynamic type of "f_extra" is the type of the value assigned
to it upon invocation (the field names "arg0", "arg1", "arg2" are made to it upon invocation (the field names "arg0", "arg1", "arg2" are made
up for illustration only, they are not accessible via reflection): up for illustration only, they are not accessible via reflection):
@ -1826,7 +1955,7 @@ as
g(x, f_extra); g(x, f_extra);
Inside g, the actual value stored in g_extra is the same as the value stored Inside g, the value stored in g_extra is the same as the value stored
in f_extra. in f_extra.
@ -2028,7 +2157,7 @@ pointer to function. Consider the type T with method M:
func (tp *T) M(a int) int; func (tp *T) M(a int) int;
var t *T; var t *T;
To construct the address of method M, we write To construct the address of method M, one writes
&t.M &t.M
@ -2187,12 +2316,6 @@ In all other cases a semicolon is required to separate two statements. Since the
is an empty statement, a statement list can always be ``terminated'' with a semicolon. is an empty statement, a statement list can always be ``terminated'' with a semicolon.
Label declarations
----
TODO write this section
Empty statements Empty statements
---- ----
@ -2584,14 +2707,14 @@ values:
Break statements Break statements
---- ----
Within a for or switch statement, a break statement terminates execution of Within a for, switch, or select statement, a break statement terminates
the innermost for or switch statement. execution of the innermost such statement.
BreakStat = "break" [ identifier ]. BreakStat = "break" [ identifier ].
If there is an identifier, it must be the label name of an enclosing If there is an identifier, it must be a label marking an enclosing
for or switch for, switch, or select statement, and that is the one whose execution
statement, and that is the one whose execution terminates. terminates.
L: for i < n { L: for i < n {
switch i { switch i {
@ -2611,13 +2734,15 @@ loop at the post statement.
The optional identifier is analogous to that of a break statement. The optional identifier is analogous to that of a break statement.
Label declaration Label declarations
---- ----
A label declaration serves as the target of a goto, break or continue statement. A label declaration serves as the target of a goto, break or continue statement.
LabelDecl = identifier ":" . LabelDecl = identifier ":" .
Example:
Error: Error:
@ -2771,7 +2896,7 @@ yields a valid value; there is no signal for overflow.
2) Between integer and floating point types, or between floating point 2) Between integer and floating point types, or between floating point
types. To avoid overdefining the properties of the conversion, for types. To avoid overdefining the properties of the conversion, for
now we define it as a ``best effort'' conversion. The conversion now it is defined as a ``best effort'' conversion. The conversion
always succeeds but the value may be a NaN or other problematic always succeeds but the value may be a NaN or other problematic
result. TODO: clarify? result. TODO: clarify?
@ -2801,7 +2926,8 @@ Allocation
The built-in function "new()" takes a type "T", optionally followed by a The built-in function "new()" takes a type "T", optionally followed by a
type-specific list of expressions. It allocates memory for a variable type-specific list of expressions. It allocates memory for a variable
of type "T" and returns a pointer of type "*T" to that variable. The of type "T" and returns a pointer of type "*T" to that variable. The
memory is initialized as described in the section on initial values. memory is initialized as described in the section on initial values
(§Program initialization and execution).
new(type [, optional list of expressions]) new(type [, optional list of expressions])
@ -3095,7 +3221,7 @@ If a type defines all methods of an interface, it
implements that interface and thus can be used where that interface is implements that interface and thus can be used where that interface is
required. Unless used through a variable of interface type, methods required. Unless used through a variable of interface type, methods
can always be statically bound (they are not ``virtual''), and incur no can always be statically bound (they are not ``virtual''), and incur no
runtime overhead compared to an ordinary function. run-time overhead compared to an ordinary function.
[OLD [OLD
Interface types, building on structures with methods, provide Interface types, building on structures with methods, provide
@ -3112,7 +3238,7 @@ structures. If a structure implements all methods of an interface, it
implements that interface and thus can be used where that interface is implements that interface and thus can be used where that interface is
required. Unless used through a variable of interface type, methods required. Unless used through a variable of interface type, methods
can always be statically bound (they are not ``virtual''), and incur no can always be statically bound (they are not ``virtual''), and incur no
runtime overhead compared to an ordinary function. run-time overhead compared to an ordinary function.
END] END]
Go has no explicit notion of classes, sub-classes, or inheritance. Go has no explicit notion of classes, sub-classes, or inheritance.