mirror of https://github.com/golang/go.git
go/types: implemented AcceptContracts flag to enable/disable contracts
AcceptContracts is a global flag (api.go) to enable/disable contracts. All go/types tests pass with the flag in either setting, i.e., all go/types tests involving contracts have been rewritten or disabled. To ensure the existing go2go code runs, AcceptContracts is set to true for now. If contracts are disabled, "comparable" is a predeclared interface (without type parameters) rather than a predeclared contract (with one type parameter). Change-Id: I2ddade9ae38d61d9edca6020f3334d2215b84f1d Reviewed-on: https://team-review.git.corp.google.com/c/golang/go2-dev/+/734984 Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
4efa3ddb87
commit
bc3ba78503
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<K> rather than anInt<K>.
|
||||
/*
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue