go/types: first cut at supporting range over generic variables

TODO
- some channel errors are not reported for generic variables
- error messages should be better

Change-Id: Ie388d6811b605645ea092481eee3a850c7ceb77b
This commit is contained in:
Robert Griesemer 2020-01-14 21:23:00 -08:00
parent c8947e760e
commit 172f10b1e2
5 changed files with 120 additions and 49 deletions

View File

@ -14,6 +14,7 @@ TODO
----------------------------------------------------------------------------------------------------
KNOWN ISSUES
- iteration over generic variables doesn't report certain channel errors (see TODOs in code)
- cannot handle mutually recursive parameterized interfaces using themselves as type bounds
- contract instantiation requires the type arguments to be type parameters from the type of function
type parameter list or enclosing contract

View File

@ -751,43 +751,24 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// determine key/value types
var key, val Type
if x.mode != invalid {
switch typ := x.typ.Underlying().(type) {
case *Basic:
if isString(typ) {
key = Typ[Int]
val = universeRune // use 'rune' name
}
case *Array:
key = Typ[Int]
val = typ.elem
case *Slice:
key = Typ[Int]
val = typ.elem
case *Pointer:
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
key = Typ[Int]
val = typ.elem
}
case *Map:
key = typ.key
val = typ.elem
case *Chan:
key = typ.elem
val = Typ[Invalid]
typ := x.typ.Underlying()
// TODO(gri) these tests need to be done also for type parameter channel types
// => move into rangeTypes and return an error message instead
if typ, _ := typ.(*Chan); typ != nil {
if typ.dir == SendOnly {
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
check.softErrorf(x.pos(), "cannot range over send-only channel %s", &x)
// ok to continue
}
if s.Value != nil {
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
check.softErrorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
// ok to continue
}
}
}
if key == nil {
check.errorf(x.pos(), "cannot range over %s", &x)
// ok to continue
key, val = rangeTypes(typ, isVarName(s.Key), isVarName(s.Value))
if key == nil {
check.softErrorf(x.pos(), "cannot range over %s", &x)
// ok to continue
}
}
// check assignment to/declaration of iteration variables
@ -869,3 +850,49 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.error(s.Pos(), "invalid statement")
}
}
func isVarName(x ast.Expr) bool {
if x == nil {
return false
}
ident, _ := unparen(x).(*ast.Ident)
return ident == nil || ident.Name != "_"
}
func rangeTypes(typ Type, wantKey, wantVal bool) (Type, Type) {
switch typ := typ.(type) {
case *Basic:
if isString(typ) {
return Typ[Int], universeRune // use 'rune' name
}
case *Array:
return Typ[Int], typ.elem
case *Slice:
return Typ[Int], typ.elem
case *Pointer:
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
return Typ[Int], typ.elem
}
case *Map:
return typ.key, typ.elem
case *Chan:
// TODO(gri) we need to move the additional channel checks here
return typ.elem, Typ[Invalid]
case *TypeParam:
first := true
var key, val Type
if typ.Interface().is(func(t Type) bool {
k, v := rangeTypes(t, true, true)
if first {
key, val = k, v
first = false
return true
}
// TODO(gri) if we fail we should return an explanatory error message
return (!wantKey || Identical(key, k)) && (!wantVal || Identical(val, v))
}) {
return key, val
}
}
return nil, nil
}

View File

@ -27,25 +27,34 @@ type II interface{
var _ I = II(nil)
*/
contract C(A, B) {
A a()
B b()
func _(type T interface{})(x T) {
for range x /* ERROR cannot range */ {}
}
//func fa(type A, B, C) (A, B, C)
//func fb(type A, B, C ABC) (A, B, C)
//func fc(type A, B, C ABC(A, B, C)) (A, B, C)
func fd(type A, B, X C(B, A)) ()
func _(type T interface{ type string, []string })(x T) {
for range x {}
for i := range x { _ = i }
for i, _ := range x { _ = i }
for i, e := range x /* ERROR cannot range */ { _ = i } // different element types
for _, e := range x /* ERROR cannot range */ {} // different element types
var e rune
_ = e
for _, (e) = range x /* ERROR cannot range */ {} // different element types
}
type tA struct{}; func (tA) a()
type tB struct{}; func (tB) b()
func _() {
//var a tA
//var b tB
//fa(a, b, 0)
//(a, b, 0)
//(a, b, 0)
//fd(a, b, 0)
fd(tA /* ERROR does not satisfy */ , tB, int)()
}
func _(type T interface{ type string, []rune, map[int]rune })(x T) {
for _, e := range x { _ = e }
for i, e := range x { _ = i; _ = e }
}
func _(type T interface{ type string, []rune, map[string]rune })(x T) {
for _, e := range x { _ = e }
for i, e := range x /* ERROR cannot range */ { _ = e } // different key types
}
func _(type T interface{ type string, chan int })(x T) {
for range x {}
for i := range x { _ = i }
for i, _ := range x { _ = i }
}

View File

@ -117,6 +117,40 @@ func _(type T interface{ type map[int]int })(x T) { _ = cap(x /* ERROR invalid a
func _(type T interface{ type chan int })(x T) { _ = cap(x) }
func _(type T interface{ type []byte, chan int })(x T) { _ = cap(x) }
// range iteration
func _(type T interface{})(x T) {
for range x /* ERROR cannot range */ {}
}
func _(type T interface{ type string, []string })(x T) {
for range x {}
for i := range x { _ = i }
for i, _ := range x { _ = i }
for i, e := range x /* ERROR cannot range */ { _ = i } // different element types
for _, e := range x /* ERROR cannot range */ {} // different element types
var e rune
_ = e
for _, (e) = range x /* ERROR cannot range */ {} // different element types
}
func _(type T interface{ type string, []rune, map[int]rune })(x T) {
for _, e := range x { _ = e }
for i, e := range x { _ = i; _ = e }
}
func _(type T interface{ type string, []rune, map[string]rune })(x T) {
for _, e := range x { _ = e }
for i, e := range x /* ERROR cannot range */ { _ = e } // different key types
}
func _(type T interface{ type string, chan int })(x T) {
for range x {}
for i := range x { _ = i }
for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
}
// type inference checks
var _ = new() /* ERROR cannot infer T */

View File

@ -221,7 +221,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
// Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
// declarations and then squash that scope into the parent scope (and report any redeclarations at
// at that time).
// that time).
scope := NewScope(check.scope, token.NoPos, token.NoPos, "function body (temp. scope)")
recvList, _ := check.collectParams(scope, recvPar, false)
params, variadic := check.collectParams(scope, ftyp.Params, true)