go/go2go: initial support for contracts

Includes a small patch to go/types to correctly check whether type
parameters are comparable.

Change-Id: Icd3443e0cd968eabdbd55f90520b0c6ad485edf8
This commit is contained in:
Ian Lance Taylor 2020-03-03 14:24:47 -08:00 committed by Robert Griesemer
parent 3370013383
commit 1bf660ee7c
5 changed files with 147 additions and 13 deletions

View File

@ -490,6 +490,16 @@ func (t *translator) instantiateExpr(ta *typeArgs, e ast.Expr) ast.Expr {
return e
case *ast.BasicLit:
return e
case *ast.FuncLit:
typ := t.instantiateExpr(ta, e.Type).(*ast.FuncType)
body := t.instantiateBlockStmt(ta, e.Body)
if typ == e.Type && body == e.Body {
return e
}
return &ast.FuncLit{
Type: typ,
Body: body,
}
case *ast.ParenExpr:
x := t.instantiateExpr(ta, e.X)
if x == e.X {
@ -518,6 +528,16 @@ func (t *translator) instantiateExpr(ta *typeArgs, e ast.Expr) ast.Expr {
Star: e.Star,
X: x,
}
case *ast.UnaryExpr:
x := t.instantiateExpr(ta, e.X)
if x == e.X {
return e
}
r = &ast.UnaryExpr{
OpPos: e.OpPos,
Op: e.Op,
X: x,
}
case *ast.BinaryExpr:
x := t.instantiateExpr(ta, e.X)
y := t.instantiateExpr(ta, e.Y)

View File

@ -208,6 +208,9 @@ func (t *translator) translate(file *ast.File) {
for j := range decl.Specs {
t.translateValueSpec(&decl.Specs[j])
}
case token.IDENT:
// A contract.
decl = nil
}
if decl != nil {
newDecls = append(newDecls, decl)
@ -331,6 +334,16 @@ func (t *translator) translateExpr(pe *ast.Expr) {
switch e := (*pe).(type) {
case *ast.Ident:
return
case *ast.BasicLit:
return
case *ast.FuncLit:
t.translateFieldList(e.Type.TParams)
t.translateFieldList(e.Type.Params)
t.translateFieldList(e.Type.Results)
t.translateBlockStmt(e.Body)
case *ast.CompositeLit:
t.translateExpr(&e.Type)
t.translateExprList(e.Elts)
case *ast.ParenExpr:
t.translateExpr(&e.X)
case *ast.BinaryExpr:
@ -348,9 +361,9 @@ func (t *translator) translateExpr(pe *ast.Expr) {
t.translateExpr(&e.Max)
case *ast.CallExpr:
t.translateExprList(e.Args)
if ftyp, ok := t.lookupType(e.Fun).(*types.Signature); ok && ftyp.TParams() != nil {
if ftyp, ok := t.lookupType(e.Fun).(*types.Signature); ok && len(ftyp.TParams()) > 0 {
t.translateFunctionInstantiation(pe)
} else if ntyp, ok := t.lookupType(e.Fun).(*types.Named); ok && ntyp.TParams() != nil {
} else if ntyp, ok := t.lookupType(e.Fun).(*types.Named); ok && len(ntyp.TParams()) > 0 && len(ntyp.TArgs()) == 0 {
t.translateTypeInstantiation(pe)
}
t.translateExpr(&e.Fun)
@ -362,11 +375,10 @@ func (t *translator) translateExpr(pe *ast.Expr) {
t.translateExpr(&e.Elt)
case *ast.StructType:
t.translateFieldList(e.Fields)
case *ast.BasicLit:
return
case *ast.CompositeLit:
t.translateExpr(&e.Type)
t.translateExprList(e.Elts)
case *ast.FuncType:
t.translateFieldList(e.TParams)
t.translateFieldList(e.Params)
t.translateFieldList(e.Results)
default:
panic(fmt.Sprintf("unimplemented Expr %T", e))
}
@ -486,7 +498,7 @@ func (t *translator) instantiatedIdent(call *ast.CallExpr) qualifiedIdent {
}
return qualifiedIdent{pkg: pn.Imported(), ident: fun.Sel}
}
panic(fmt.Sprintf("instantiated object %v is not an identifier", call.Fun))
panic(fmt.Sprintf("instantiated object %T %v is not an identifier", call.Fun, call.Fun))
}
// instantiationTypes returns the type arguments of an instantiation.

View File

@ -13,11 +13,11 @@ import (
// lookupType returns the types.Type for an AST expression.
// Returns nil if the type is not known.
func (t *translator) lookupType(e ast.Expr) types.Type {
if t, ok := t.info.Types[e]; ok {
return t.Type
if typ, ok := t.info.Types[e]; ok {
return typ.Type
}
if t, ok := t.types[e]; ok {
return t
if typ, ok := t.types[e]; ok {
return typ
}
return nil
}
@ -64,6 +64,10 @@ func (t *translator) instantiateType(ta *typeArgs, typ types.Type) types.Type {
// This should only be called from instantiateType.
func (t *translator) doInstantiateType(ta *typeArgs, typ types.Type) types.Type {
switch typ := typ.(type) {
case *types.Named:
return typ
case *types.Basic:
return typ
case *types.TypeParam:
if instType, ok := ta.typ(typ); ok {
return instType
@ -75,7 +79,7 @@ func (t *translator) doInstantiateType(ta *typeArgs, typ types.Type) types.Type
if elem == instElem {
return typ
}
return types.NewSlice(elem)
return types.NewSlice(instElem)
case *types.Signature:
params := t.instantiateTypeTuple(ta, typ.Params())
results := t.instantiateTypeTuple(ta, typ.Results())

View File

@ -85,6 +85,8 @@ func Comparable(T Type) bool {
return true
case *Array:
return Comparable(t.elem)
case *TypeParam:
return t.Interface().is(Comparable)
}
return false
}

96
test/gen/g006.go2 Normal file
View File

@ -0,0 +1,96 @@
// run
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"math"
"os"
"sort"
)
contract Ordered(T) {
T int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64,
string
}
type orderedSlice(type Elem Ordered) []Elem
func (s orderedSlice(Elem)) Len() int { return len(s) }
func (s orderedSlice(Elem)) Less(i, j int) bool {
if s[i] < s[j] {
return true
}
isNaN := func(f Elem) bool { return f != f }
if isNaN(s[i]) && !isNaN(s[j]) {
return true
}
return false
}
func (s orderedSlice(Elem)) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func OrderedSlice(type Elem Ordered)(s []Elem) {
sort.Sort(orderedSlice(Elem)(s))
}
var ints = []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
var float64s = []float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
var strings = []string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
func TestSortOrderedInts() bool {
return testOrdered(int)("ints", ints, sort.Ints)
}
func TestSortOrderedFloat64s() bool {
return testOrdered(float64)("float64s", float64s, sort.Float64s)
}
func TestSortOrderedStrings() bool {
return testOrdered(string)("strings", strings, sort.Strings)
}
func testOrdered(type Elem Ordered)(name string, s []Elem, sorter func([]Elem)) bool {
s1 := make([]Elem, len(s))
copy(s1, s)
s2 := make([]Elem, len(s))
copy(s2, s)
OrderedSlice(Elem)(s1)
sorter(s2)
ok := true
if !sliceEq(Elem)(s1, s2) {
fmt.Printf("%s: got %v, want %v", name, s1, s2)
ok = false
}
for i := len(s1) - 1; i > 0; i-- {
if s1[i] < s1[i-1] {
fmt.Printf("%s: element %d (%v) < element %d (%v)", name, i, s1[i], i - 1, s1[i - 1])
ok = false
}
}
return ok
}
func sliceEq(type Elem Ordered)(s1, s2[]Elem) bool {
for i, v1 := range s1 {
v2 := s2[i]
if v1 != v2 {
isNaN := func(f Elem) bool { return f != f }
if !isNaN(v1) || !isNaN(v2) {
return false
}
}
}
return true
}
func main() {
if !TestSortOrderedInts() || !TestSortOrderedFloat64s() || !TestSortOrderedStrings() {
os.Exit(1)
}
}