mirror of https://github.com/golang/go.git
go/types: implement interface embedding of interfaces with types
This follows the approach used for methods, but there is no error reporting yet if a type is (explicitly) declared multiple times in an interface. Change-Id: I52428174ae278577a7c538b0817c6fb7af1c369e
This commit is contained in:
parent
d4338964b3
commit
bc236133bf
|
|
@ -2,16 +2,14 @@ This file works as a sort of notebook/implementation log. It replaces my noteboo
|
|||
so we have a better track record. I only switched to this file recently, hence it is incomplete.
|
||||
|
||||
TODO
|
||||
- type assertions on/against parameterized types
|
||||
- no need for error messages where a _ type parameter cannot be inferred (_ cannot be used)
|
||||
- use Underlying() to return a type parameter's bound? investigate!
|
||||
- better error message when declaring a contract local to a function (parser gets out of sync)
|
||||
- if type parameters are repeated in recursive instantiation, they must be the same order (not yet checked)
|
||||
- interface embedding doesn't take care of literal type constraints yet
|
||||
(need an allTypes list, like we have an allMethods list?)
|
||||
- type assertions on/against parameterized types
|
||||
- use []*TypeParam for tparams in subst? (unclear)
|
||||
|
||||
OPEN ISSUES
|
||||
- a contract that is used earlier than its declaration may not be set up yet
|
||||
- instantiating a parameterized function type w/o value or result parameters may have unexpected side-effects
|
||||
(we don't make a copy of the signature in some cases) - investigate
|
||||
- using a contract and enumerating type arguments currently leads to an error (e.g. func f(type T C(T)) (x T) ... )
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@ 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: PROTOTYPE. THERE ARE KNOWN (AND UNKNOWN) BUGS.
|
||||
CAUTION: PROTOTYPE. THERE ARE KNOWN AND UNKNOWN BUGS.
|
||||
|
||||
Read and use this code at your own risk.
|
||||
|
||||
STATUS
|
||||
|
||||
With some exceptions (see below), the most complex aspects of the contracts
|
||||
proposal have been implemented and somewhat tested (but expect bugs).
|
||||
go/types can now type-check all the *.go2 test files in the testdata and
|
||||
examples directories.
|
||||
Most aspects of the contracts proposal have been implemented and somewhat
|
||||
tested (but expect bugs). go/types can now type-check all the *.go2 test
|
||||
files in the testdata and examples directories.
|
||||
|
||||
ALTERNATIVE NOTATION
|
||||
|
||||
|
|
@ -72,19 +71,17 @@ and interfaces and the question if they can be mixed and how.
|
|||
Interfaces are the fundamental underlying typing mechanism;
|
||||
contracts are syntactic sugar that may improve readability.
|
||||
|
||||
KNOWN ISSUES
|
||||
MAJOR KNOWN ISSUES
|
||||
|
||||
- importing of packages exporting generic code is not implemented
|
||||
(and won't be implemented in this prototype)
|
||||
- various type-specific operations (such as indexing, sending a
|
||||
message, type assertions, etc.) on expressions of a generic type
|
||||
don't work yet (but are relatively easy to implement going forward)
|
||||
- error messages are reasonable but expect them to be significantly
|
||||
better in a real implementation (the subscript numbers on type
|
||||
parameters are there to visually identify different parameters
|
||||
with the same name)
|
||||
- embedding of interfaces ignores (the notationally new) type
|
||||
lists in interfaces
|
||||
- various type-specific operations (such as indexing, sending a
|
||||
message, etc.) on expressions of a generic type don't work
|
||||
yet (but are relatively easy to implement, going forward)
|
||||
- gofmt works only partly with parameterized code
|
||||
|
||||
See also the NOTES file for a more up-to-date documentation of the
|
||||
|
|
@ -93,12 +90,21 @@ current state and issues.
|
|||
TO PLAY WITH THIS PROTOTYPE
|
||||
|
||||
- Cherry-pick this CL on top of tip (the cherry-pick was tested with
|
||||
tip at 4d5bb9c609):
|
||||
tip at bf26847240):
|
||||
|
||||
git fetch "https://go.googlesource.com/go" ... && git cherry-pick FETCH_HEAD
|
||||
|
||||
(replace the ... with the respective information from Gerrit's CL page)
|
||||
|
||||
- In the go/types directory, verify that the tests run:
|
||||
|
||||
go test
|
||||
|
||||
(If this causes a vet error, run "go test -vet off" instead.)
|
||||
|
||||
Note: This version of go/types is built in "debug" mode (see check.go:17)
|
||||
and does extra checks. As a consequence it may run a bit slower than usual.
|
||||
|
||||
- In the go/types directory, build the gotype command:
|
||||
|
||||
go build gotype.go
|
||||
|
|
@ -127,5 +133,6 @@ Updates:
|
|||
12/15/2019: Significant progress with much of the functionality present.
|
||||
12/19/2019: Several bugs around type bounds checking fixed; more complex examples.
|
||||
12/20/2019: Treat contracts as objects, not types anymore. Various bug fixes and more examples.
|
||||
1/2/2020: Implemented contract embedding. Various bug fixes.
|
||||
|
||||
Change-Id: I29839b5e95d7050fce1dcb3334d3d324883cf76f
|
||||
|
|
|
|||
|
|
@ -677,7 +677,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
|||
// construct a suitable new type parameter
|
||||
tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
|
||||
ptyp := check.NewTypeParam(tpar, 0, nil) // assigns type to tpar as a side-effect
|
||||
ptyp.bound = &Interface{allMethods: markComplete, types: resTypes}
|
||||
ptyp.bound = &Interface{types: resTypes, allMethods: markComplete, allTypes: resTypes}
|
||||
|
||||
return ptyp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ func (check *Checker) typeConstraint(typ Type, why *string) bool {
|
|||
check.typeConstraint(t.params, why) &&
|
||||
check.typeConstraint(t.results, why)
|
||||
case *Interface:
|
||||
t.assertCompleteness()
|
||||
for _, m := range t.allMethods {
|
||||
if !check.typeConstraint(m.typ, why) {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -513,12 +513,12 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
|
|||
// In case of a type parameter, conversion must succeed against
|
||||
// all types enumerated by the the type parameter bound.
|
||||
if t, _ := target.Underlying().(*TypeParam); t != nil {
|
||||
types := t.Interface().types
|
||||
types := t.Interface().allTypes
|
||||
if len(types) == 0 {
|
||||
goto Error
|
||||
}
|
||||
|
||||
for _, t := range t.Interface().types {
|
||||
for _, t := range types {
|
||||
check.convertUntypedInternal(x, t)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
|
|
|
|||
|
|
@ -144,9 +144,7 @@ func isParameterized(typ Type, seen map[Type]bool) (res bool) {
|
|||
return isParameterized(t.params, seen) || isParameterized(t.results, seen)
|
||||
|
||||
case *Interface:
|
||||
if t.allMethods == nil {
|
||||
panic("incomplete method")
|
||||
}
|
||||
t.assertCompleteness()
|
||||
for _, m := range t.allMethods {
|
||||
if isParameterized(m.typ, seen) {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -108,14 +108,14 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
|||
}
|
||||
|
||||
// targ's underlying type must also be one of the interface types listed, if any
|
||||
if len(iface.types) == 0 {
|
||||
if len(iface.allTypes) == 0 {
|
||||
break // nothing to do
|
||||
}
|
||||
|
||||
// if targ is itself a type parameter, each of its possible types must be in the
|
||||
// list of iface types (i.e., the targ type list must be a subset of the iface types)
|
||||
if targ, _ := targ.Underlying().(*TypeParam); targ != nil {
|
||||
for _, t := range targ.Interface().types {
|
||||
for _, t := range targ.Interface().allTypes {
|
||||
if !iface.includes(t.Underlying()) {
|
||||
// TODO(gri) match this error message with the one below (or vice versa)
|
||||
check.softErrorf(pos, "%s does not satisfy %s (missing type %s)", targ, tpar.bound, t)
|
||||
|
|
@ -126,7 +126,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
|||
}
|
||||
|
||||
// otherwise, targ's underlying type must also be one of the interface types listed, if any
|
||||
if len(iface.types) > 0 {
|
||||
if len(iface.allTypes) > 0 {
|
||||
// TODO(gri) must it be the underlying type, or should it just be the type? (spec question)
|
||||
if !iface.includes(targ.Underlying()) {
|
||||
check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, targ, iface)
|
||||
|
|
@ -229,11 +229,12 @@ func (subst *subster) typ(typ Type) Type {
|
|||
|
||||
case *Interface:
|
||||
methods, mcopied := subst.funcList(t.methods)
|
||||
embeddeds, ecopied := subst.typeList(t.embeddeds)
|
||||
types, tcopied := subst.typeList(t.types)
|
||||
if mcopied || ecopied || tcopied {
|
||||
iface := &Interface{methods: methods, embeddeds: embeddeds, types: types}
|
||||
iface.Complete()
|
||||
embeddeds, ecopied := subst.typeList(t.embeddeds)
|
||||
if mcopied || tcopied || ecopied {
|
||||
iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
|
||||
subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
|
||||
subst.check.completeInterface(token.NoPos, iface)
|
||||
return iface
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,19 @@ type myTab struct{ myTa }
|
|||
|
||||
func (myTab) b()
|
||||
|
||||
contract E5(T) {
|
||||
T int
|
||||
}
|
||||
|
||||
contract E6(T) {
|
||||
E5(T)
|
||||
E5(T)
|
||||
}
|
||||
|
||||
func _(type T E6)(x T) T {
|
||||
return x + 1
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Contract satisfaction
|
||||
|
||||
|
|
|
|||
|
|
@ -4,35 +4,10 @@
|
|||
|
||||
package p
|
||||
|
||||
contract C1(a, b, c) {
|
||||
a a()
|
||||
b b()
|
||||
c c()
|
||||
}
|
||||
type B1 interface{type int}
|
||||
|
||||
contract C2(a, b) {
|
||||
C1(a, b, b)
|
||||
}
|
||||
type T3(type P B1) P
|
||||
|
||||
func f(type T, _ C2)(x T)
|
||||
type B2 interface{type int, string}
|
||||
|
||||
type X struct{}
|
||||
|
||||
func (X) a()
|
||||
//func (X) b()
|
||||
//func (X) c()
|
||||
|
||||
func _() {
|
||||
var x X
|
||||
f(X, int)(x)
|
||||
}
|
||||
|
||||
contract C3(x) {
|
||||
C4(x)
|
||||
}
|
||||
|
||||
contract C4(x) {
|
||||
// C3(x)
|
||||
}
|
||||
|
||||
contract C(A) { A C /* ERROR use of contract */ }
|
||||
func _(type P B2)(x T3(P /* ERROR missing type string */ ))
|
||||
|
|
@ -245,16 +245,16 @@ func (s *Signature) Variadic() bool { return s.variadic }
|
|||
// An Interface represents an interface type.
|
||||
type Interface struct {
|
||||
methods []*Func // ordered list of explicitly declared methods
|
||||
types []Type // list of explicitly declared types (for contracts)
|
||||
embeddeds []Type // ordered list of explicitly embedded types
|
||||
|
||||
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
|
||||
|
||||
types []Type // for contracts
|
||||
allTypes []Type // list of types declared with or embedded in this interface
|
||||
}
|
||||
|
||||
// is reports whether interface t represents types that all satisfy pred.
|
||||
func (t *Interface) is(pred func(Type) bool) bool {
|
||||
for _, t := range t.types {
|
||||
for _, t := range t.allTypes {
|
||||
if !pred(t) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -363,7 +363,7 @@ func (t *Interface) Empty() bool { t.assertCompleteness(); return len(t.allMetho
|
|||
|
||||
// includes reports whether the interface t includes the type typ.
|
||||
func (t *Interface) includes(typ Type) bool {
|
||||
for _, t := range t.types {
|
||||
for _, t := range t.allTypes {
|
||||
if Identical(t, typ) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -403,12 +403,16 @@ func (t *Interface) Complete() *Interface {
|
|||
addMethod(m, true)
|
||||
}
|
||||
|
||||
var types []Type
|
||||
types = append(types, t.types...)
|
||||
|
||||
for _, typ := range t.embeddeds {
|
||||
typ := typ.Underlying().(*Interface)
|
||||
typ.Complete()
|
||||
for _, m := range typ.allMethods {
|
||||
addMethod(m, false)
|
||||
}
|
||||
types = append(types, typ.types...)
|
||||
}
|
||||
|
||||
for i := 0; i < len(todo); i += 2 {
|
||||
|
|
@ -423,6 +427,7 @@ func (t *Interface) Complete() *Interface {
|
|||
sort.Sort(byUniqueMethodName(methods))
|
||||
t.allMethods = methods
|
||||
}
|
||||
t.allTypes = types
|
||||
|
||||
return t
|
||||
}
|
||||
|
|
@ -541,7 +546,9 @@ func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypePa
|
|||
}
|
||||
|
||||
func (t *TypeParam) Interface() *Interface {
|
||||
return t.bound.Underlying().(*Interface)
|
||||
iface := t.bound.Underlying().(*Interface)
|
||||
iface.Complete() // TODO(gri) should we use check.completeInterface instead?
|
||||
return iface
|
||||
}
|
||||
|
||||
// Implementations for Type methods.
|
||||
|
|
|
|||
|
|
@ -178,10 +178,10 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
|||
writeSignature(buf, m.typ.(*Signature), qf, visited)
|
||||
empty = false
|
||||
}
|
||||
if !empty && len(t.types) > 0 {
|
||||
if !empty && len(t.allTypes) > 0 {
|
||||
buf.WriteString("; ")
|
||||
}
|
||||
for i, typ := range t.types {
|
||||
for i, typ := range t.allTypes {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -630,7 +630,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
|
|||
// type constraints
|
||||
ityp.types = check.collectTypeConstraints(iface.Pos(), ityp.types, iface.Types)
|
||||
|
||||
if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
|
||||
if len(ityp.methods) == 0 && len(ityp.types) == 0 && len(ityp.embeddeds) == 0 {
|
||||
// empty interface
|
||||
ityp.allMethods = markComplete
|
||||
return
|
||||
|
|
@ -668,7 +668,7 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
|
|||
check.indent++
|
||||
defer func() {
|
||||
check.indent--
|
||||
check.trace(pos, "=> %s", ityp)
|
||||
check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
@ -718,6 +718,11 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
|
|||
addMethod(m.pos, m, true)
|
||||
}
|
||||
|
||||
// collect types
|
||||
// TODO(gri) report error for multiply explicitly declared identical types
|
||||
var types []Type
|
||||
types = append(types, ityp.types...)
|
||||
|
||||
posList := check.posMap[ityp]
|
||||
for i, typ := range ityp.embeddeds {
|
||||
pos := posList[i] // embedding position
|
||||
|
|
@ -731,12 +736,14 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
|
|||
for _, m := range typ.allMethods {
|
||||
addMethod(pos, m, false) // use embedding position pos rather than m.pos
|
||||
}
|
||||
types = append(types, typ.allTypes...)
|
||||
}
|
||||
|
||||
if methods != nil {
|
||||
sort.Sort(byUniqueMethodName(methods))
|
||||
ityp.allMethods = methods
|
||||
}
|
||||
ityp.allTypes = types
|
||||
}
|
||||
|
||||
// byUniqueTypeName named type lists can be sorted by their unique type names.
|
||||
|
|
|
|||
Loading…
Reference in New Issue