diff --git a/src/go/types/api.go b/src/go/types/api.go index 510a0e736c..0abb739991 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -34,6 +34,9 @@ import ( "go/token" ) +// If AcceptContracts is set, contracts are accepted. +const AcceptContracts = true + // An Error describes a type-checking error; it implements the error interface. // A "soft" error is an error that still permits a valid interpretation of a // package (such as "unused variable"); "hard" errors may lead to unpredictable diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index f8045e4615..aa402b97e5 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -104,7 +104,7 @@ var tests = [][]string{ {"testdata/typeparams.go2"}, {"testdata/typeinst.go2"}, {"testdata/typeinst2.go2"}, - {"testdata/contracts.go2"}, + // {"testdata/contracts.go2"}, // disabled for now {"testdata/issues.go2"}, {"testdata/todos.go2"}, @@ -116,7 +116,7 @@ var tests = [][]string{ {"testdata/linalg.go2"}, // Go 2 prototype examples - {"examples/contracts.go2"}, + // {"examples/contracts.go2"}, disabled for now {"examples/functions.go2"}, {"examples/methods.go2"}, {"examples/types.go2"}, diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 17e7133787..1b24b491d5 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -205,6 +205,11 @@ func (check *Checker) objDecl(obj Object, def *Named) { // functions may be recursive - no need to track dependencies check.funcDecl(obj, d) case *Contract: + if !AcceptContracts { + check.errorf(obj.pos, "contracts are not accepted") + obj.typ = Typ[Invalid] + break + } check.contractDecl(obj, d.cdecl) default: unreachable() diff --git a/src/go/types/testdata/issues.go2 b/src/go/types/testdata/issues.go2 index 69aa2714fd..eeeed730b2 100644 --- a/src/go/types/testdata/issues.go2 +++ b/src/go/types/testdata/issues.go2 @@ -16,6 +16,7 @@ import "io" // the numbers for anInt and twoInt (which embedds anInt). // The fix simply uses the instantiated non-parameterized // underlying interface of atInt rather than anInt. +/* contract anInt(T) { T int } @@ -29,8 +30,10 @@ func f(type K, V twoInt)() func _ () { f(int, int)() } +*/ // This is the original (simplified) program causing the same issue. +/* contract onecomparable(T) { T int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, @@ -49,6 +52,7 @@ func _() { var m map[int]int Equal(m, nil) } +*/ // Interfaces are always comparable (though the comparison may panic at runtime). func eql(type T comparable)(x, y T) bool { @@ -69,21 +73,23 @@ func _() { // the pointer in the implementation of the method lookup because // the type bound of T is an interface an pointer to interface types // have no methods and then the lookup would fail. -contract C(T) { - T m() +type C(type T) interface { + m() } -// using contract C +// using type bound C func _(type T C)(x *T) { x.m() } // using an interface as bound +// TODO(gri) this is now the same as above (no contracts anymore) func _(type T interface{ m() })(x *T) { x.m() } // This is the original (simplified) program causing the same issue. +/* type GraphP(type Node, Edge GP) struct { nodes []*Node } @@ -95,6 +101,7 @@ contract GP(Node, Edge) { func (g *GraphP(Node, Edge)) Edges(n *Node) []*Edge { return n.Edges() } +*/ // In a generic function body all method calls will be pointer method calls. // If necessary, the function body will insert temporary variables, not seen @@ -114,6 +121,7 @@ func _() { } // This is the original (simplified) program causing the same issue. +/* func NewP(type Node, Edge GP)(nodes []*Node) *GraphP(Node, Edge) { return &GraphP(Node, Edge){nodes: nodes} } @@ -124,6 +132,7 @@ type E struct{} func F() { _ = NewP(N, E)(nil) } +*/ // When a type parameter is used as an argument to instantiate a parameterized // type with a type list constraint, all of the type argument's types in its @@ -136,6 +145,7 @@ func _(type P)() { } // This is the original (simplified) program causing the same issue. +/* contract Unsigned(T) { T uint } @@ -148,14 +158,15 @@ func (u T2(U)) Add1() U { return u.s + 1 } -func NewT2(type U)() T2(U /* ERROR U has no type constraints */ ) { - return T2(U /* ERROR U has no type constraints */ ){} +func NewT2(type U)() T2(U /- ERROR U has no type constraints -/ ) { + return T2(U /- ERROR U has no type constraints -/ ){} } func _() { u := NewT2(string)() _ = u.Add1() } +*/ // When we encounter an instantiated type such as Elem(T) we must // not "expand" the instantiation when the type to be instantiated diff --git a/src/go/types/testdata/linalg.go2 b/src/go/types/testdata/linalg.go2 index 80ca046a48..d2c4bb1367 100644 --- a/src/go/types/testdata/linalg.go2 +++ b/src/go/types/testdata/linalg.go2 @@ -6,13 +6,13 @@ package linalg import "math" -// Numeric is a contract that matches any numeric type. +// Numeric is type bound that matches any numeric type. // It would likely be in a contracts package in the standard library. -contract Numeric(T) { - T int; T int8; T int16; T int32; T int64 - T uint; T uint8; T uint16; T uint32; T uint64; T uintptr - T float32; T float64 - T complex64; T complex128 +type Numeric interface { + type int, int8, int16, int32, int64 + type uint, uint8, uint16, uint32, uint64, uintptr + type float32, float64 + type complex64, complex128 } func DotProduct(type T Numeric)(s1, s2 []T) T { @@ -27,10 +27,10 @@ func DotProduct(type T Numeric)(s1, s2 []T) T { } // NumericAbs matches numeric types with an Abs method. -contract NumericAbs(T) { - Numeric(T) +type NumericAbs(type T) interface { + Numeric - T Abs() T + Abs() T } // AbsDifference computes the absolute value of the difference of @@ -40,16 +40,16 @@ func AbsDifference(type T NumericAbs)(a, b T) T { return d.Abs() } -// OrderedNumeric matches numeric types that support the < operator. -contract OrderedNumeric(T) { - T int; T int8; T int16; T int32; T int64 - T uint; T uint8; T uint16; T uint32; T uint64; T uintptr - T float32; T float64 +// OrderedNumeric is a type bound that matches numeric types that support the < operator. +type OrderedNumeric interface { + type int, int8, int16, int32, int64 + type uint, uint8, uint16, uint32, uint64, uintptr + type float32, float64 } -// Complex matches the two complex types, which do not have a < operator. -contract Complex(T) { - T complex64; T complex128 +// Complex is a type bound that matches the two complex types, which do not have a < operator. +type Complex interface { + type complex64, complex128 } // OrderedAbs is a helper type that defines an Abs method for diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 776f869cf0..ac58657eb9 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -1,15 +1,21 @@ package p -type Adder(type T) interface { - Add(T) T +// These are only ok if AcceptContracts = false. +// (The comparable contract cannot be embedded in Bound.) +/* +func _(type T comparable)(x, y T) bool { + return x == y || x != y } -// We don't need to explicitly instantiate the Adder bound -// if we have exactly one type parameter. -func Sum(type T Adder)(list []T) T { - var sum T - for _, x := range list { - sum = sum.Add(x) - } - return sum +type Bound interface { + comparable } + +func _(type T Bound)(x, y T) bool { + return x == y || x != y +} + +func _(type A, B Bound)(a1, a2 A, b1, b2 B) bool { + return a1 == a2 || b1 != b2 +} +*/ diff --git a/src/go/types/testdata/todos.go2 b/src/go/types/testdata/todos.go2 index 8365b33699..e1c1ecbdee 100644 --- a/src/go/types/testdata/todos.go2 +++ b/src/go/types/testdata/todos.go2 @@ -9,9 +9,12 @@ package p // Pointer designation for type parameters is not yet supported. +// TODO(gri) Do we need to do something about this now that we have only interfaces? +/* contract _C(T) { - * /* ERROR not yet supported */ T m() + *T m() } +*/ // Indexing on generic types containing type parameters in their type list // is not yet supported. diff --git a/src/go/types/testdata/typeinst2.go2 b/src/go/types/testdata/typeinst2.go2 index 1413f9d975..1dcc62d789 100644 --- a/src/go/types/testdata/typeinst2.go2 +++ b/src/go/types/testdata/typeinst2.go2 @@ -22,7 +22,7 @@ type ( func _() { var x1 T1(int) var x2 T2(int, float32) - + x1.f1.f2 = 0 x1.f1 = x2 } @@ -81,8 +81,8 @@ func (it Iterator(K)) Next() K { // A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence) -contract NumericAbs(T) { - T Abs() T +type NumericAbs(type T) interface { + Abs() T } func AbsDifference(type T NumericAbs)(x T) diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index 36381db62a..1b140ac1ef 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -55,8 +55,8 @@ func _(type T interface{type int, float32, bool})(x, y T) bool { return x /* ERR func _(type T C1)(x, y T) bool { return x /* ERROR cannot compare */ < y } func _(type T C2)(x, y T) bool { return x < y } -contract C1(T) {} -contract C2(T) { T int, float32 } +type C1(type T) interface{} +type C2(type T) interface{ type int, float32 } func new(type T)() *T { var x T @@ -365,10 +365,10 @@ func _(type T I3)(x I3, p T) { } // error messages related to type bounds mention those bounds -contract C(P) {} +type C(type P) interface{} func _(type P C) (x P) { - x.m /* ERROR contract C has no method m */ () + x.m /* ERROR x.m undefined */ () } type I interface {} diff --git a/src/go/types/universe.go b/src/go/types/universe.go index a77826955b..36fdec2f61 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -188,7 +188,7 @@ func DefPredeclaredTestFuncs() { def(newBuiltin(_Trace)) } -func defPredeclaredContracts() { +func defPredeclaredComparableContract() { // The "comparable" contract can be envisioned as defined like // // contract comparable(T) { @@ -233,6 +233,33 @@ func defPredeclaredContracts() { def(obj) } +func defPredeclaredComparableInterface() { + // The "comparable" interface can be envisioned as defined like + // + // type comparable interface { + // == () untyped bool + // != () untyped bool + // } + // + // == and != cannot be user-declared but we can declare + // a magic method == and check for its presence when needed. + + // Define interface { == () }. We don't care about the signature + // for == so leave it empty except for the receiver, which is + // set up later to match the usual interface method assumptions. + sig := new(Signature) + eql := NewFunc(token.NoPos, nil, "==", sig) + iface := NewInterfaceType([]*Func{eql}, nil).Complete() + + // set up the defined type for the interface + obj := NewTypeName(token.NoPos, nil, "comparable", nil) + named := NewNamed(obj, iface, nil) + obj.color_ = black + sig.recv = NewVar(token.NoPos, nil, "", named) // complete == signature + + def(obj) +} + func init() { Universe = NewScope(nil, token.NoPos, token.NoPos, "universe") Unsafe = NewPackage("unsafe", "unsafe") @@ -242,7 +269,11 @@ func init() { defPredeclaredConsts() defPredeclaredNil() defPredeclaredFuncs() - defPredeclaredContracts() + if AcceptContracts { + defPredeclaredComparableContract() + } else { + defPredeclaredComparableInterface() + } universeIota = Universe.Lookup("iota").(*Const) universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)