diff --git a/src/go/types/README b/src/go/types/README index 362769b65c..d5ee31c625 100644 --- a/src/go/types/README +++ b/src/go/types/README @@ -2,7 +2,7 @@ This code contains changes to go/types and the go/* support libraries to type-check generic code as outlined in the latest contracts proposal and presented by Ian Lance Taylor at GopherCon 2019 in San Diego. -CAUTION: EARLY PROTOTYPE. A LOT IS STILL MISSING. THERE ARE MANY BUGS. +CAUTION: EARLY PROTOTYPE. MISSING PIECES. THERE ARE BUGS. Read and use the code at your own risk. @@ -10,31 +10,26 @@ That said, the go/parser and go/ast changes are working and pass tests including all the larger examples in the latest contracts design doc. Look for the *.go2 files in go/parser/testdata. -gofmt does not work with parameterized code yet. +gofmt works only partly with parameterized code. -The type-checker is starting to work but has still many problems. +The type-checker is starting to work but is not complete. I will update this CL from time to time as progress happens. Specifically, the following pieces (and more) are missing from type- checking or lead to unexpected behavior: - Importing of packages with type parameters or contracts. -- Type-checking of contracts and type parameter lists with contracts. - For instance, because contracts are not yet type-checked, maps where - the key type is a type parameter will lead to an error because the - type checker doesn't know whether the key type is comparable or not. -- Parameterized alias types. -- Type instantiation is not always working as expected, leading to - some odd error messages. -- Methods with parameterized receiver types have only recently started - to work; there are a few issues around them with proper type - instantiation. +- Type-checking of contracts is limited to contracts with methods. +- Methods with parameterized receiver types are not implemented yet. The following is "working" (as in: can be type-checked without errors): -- Declaration and use of simple parameterized types without contracts. -- Declaration and use (calls) of simple parameterized functions without - contracts, including type inference from function arguments. +- Declaration and use of parameterized types. +- Declaration and use (calls) of parameterized functions, + including type inference from function arguments. +- Some larger tests pass mostly (see testdata/*.go2 files). + +Some code may look like it's working, but it may simply not do anything. Error messages, where present, are in usable condition but expect them to be significantly better in a more complete implementation. @@ -42,7 +37,7 @@ them to be significantly better in a more complete implementation. To play with this prototype: - Cherry-pick this CL on top of tip (the cherry-pick was tested with - tip at 919594830f17): + tip at 4983a0b75b40): git fetch "https://go.googlesource.com/go" ... && git cherry-pick FETCH_HEAD diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 11d277dcdd..baa3b30d00 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -110,6 +110,7 @@ var tests = [][]string{ {"testdata/slices.go2"}, {"testdata/chans.go2"}, {"testdata/map.go2"}, + {"testdata/map2.go2"}, } var fset = token.NewFileSet() diff --git a/src/go/types/testdata/map2.go2 b/src/go/types/testdata/map2.go2 new file mode 100644 index 0000000000..96981d7c3a --- /dev/null +++ b/src/go/types/testdata/map2.go2 @@ -0,0 +1,139 @@ +// This file is like map.go2, but instead if importing chans, it contains +// the necessary functionality at the end of the file. + +// Package orderedmap provides an ordered map, implemented as a binary tree. +package orderedmap + +// Map is an ordered map. +type Map(type K, V) struct { + root *node(K, V) + compare func(K, K) int +} + +// node is the type of a node in the binary tree. +type node(type K, V) struct { + key K + val V + left, right *node(K, V) +} + +// New returns a new map. +func New(type K, V)(compare func(K, K) int) *Map(K, V) { + return &Map(K, V){compare: compare} +} + +// find looks up key in the map, and returns either a pointer +// to the node holding key, or a pointer to the location where +// such a node would go. +func (m *Map(K, V)) find(key K) **node(K, V) { + pn := &m.root + for *pn != nil { + switch cmp := m.compare(key, (*pn).key); { + case cmp < 0: + pn = &(*pn).left + case cmp > 0: + pn = &(*pn).right + default: + return pn + } + } + return pn +} + +// Insert inserts a new key/value into the map. +// If the key is already present, the value is replaced. +// Returns true if this is a new key, false if already present. +func (m *Map(K, V)) Insert(key K, val V) bool { + pn := m /* ERROR not implemented */ .find(key) + if *pn != nil { + (*pn).val = val + return false + } + *pn = &node(K, V){key: key, val: val} + return true +} + +// Find returns the value associated with a key, or zero if not present. +// The found result reports whether the key was found. +func (m *Map(K, V)) Find(key K) (V, bool) { + pn := m /* ERROR not implemented */ .find(key) + if *pn == nil { + var zero V // see the discussion of zero values, above + return zero, false + } + return (*pn).val, true +} + +// keyValue is a pair of key and value used when iterating. +type keyValue(type K, V) struct { + key K + val V +} + +// InOrder returns an iterator that does an in-order traversal of the map. +func (m *Map(K, V)) InOrder() *Iterator(K, V) { + sender, receiver := chans_Ranger(keyValue(K, V))() + var f func(*node(K, V)) bool + f = func(n *node(K, V)) bool { + if n == nil { + return true + } + // Stop sending values if sender.Send returns false, + // meaning that nothing is listening at the receiver end. + return f(n.left) && + // TODO + // sender.Send(keyValue(K, V){n.key, n.val}) && + f(n.right) + } + go func() { + f(m.root) + sender /* ERROR not implemented */ .Close() + }() + // TODO(gri) The design draft doesn't require that we repeat + // the type parameters here. Fix the implementation. + _ = receiver + panic(0) + //return &Iterator(K, V){receiver} + // return &Iterator{receiver} +} + +// Iterator is used to iterate over the map. +type Iterator(type K, V) struct { + r *chans_Receiver(keyValue(K, V)) +} + +// Next returns the next key and value pair, and a boolean indicating +// whether they are valid or whether we have reached the end. +func (it *Iterator(K, V)) Next() (K, V, bool) { + keyval, ok := it /* ERROR not implemented */ .r.Next() + if !ok { + var zerok K + var zerov V + return zerok, zerov, false + } + return keyval.key, keyval.val, true +} + +// chans + +func chans_Ranger(type T)() (*chans_Sender(T), *chans_Receiver(T)) + +// A sender is used to send values to a Receiver. +type chans_Sender(type T) struct { + values chan<- T + done <-chan bool +} + +func (s *chans_Sender(T)) Close() { + close(s.values) +} + +type chans_Receiver(type T) struct { + values <-chan T + done chan<- bool +} + +func (r *chans_Receiver(T)) Next() (T, bool) { + v, ok := <-r.values + return v, ok +} \ No newline at end of file