1
0
mirror of https://github.com/golang/go synced 2024-11-23 00:30:07 -07:00

[dev.typeparams] import stmt changes from dev.go2go

Import logic for typechecking statements involving generics from the
dev.go2go branch.  Notably, range type checking was simplified in
dev.go2go, resulting in the removal of the _InvalidChanRange error code.

Change-Id: I84c2665226c2b9b74e85f7fb6df257b0a292e5d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/282120
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
Rob Findley 2021-01-07 11:13:56 -05:00 committed by Robert Findley
parent eb53a6c7cf
commit 1ce0854157
3 changed files with 101 additions and 56 deletions

View File

@ -1038,18 +1038,6 @@ const (
// }
_InvalidPostDecl
// _InvalidChanRange occurs when a send-only channel used in a range
// expression.
//
// Example:
// func sum(c chan<- int) {
// s := 0
// for i := range c {
// s += i
// }
// }
_InvalidChanRange
// _InvalidIterVar occurs when two iteration variables are used while ranging
// over a channel.
//

View File

@ -49,6 +49,11 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
check.error(atPos(body.Rbrace), _MissingReturn, "missing return")
}
// TODO(gri) Should we make it an error to declare generic functions
// where the type parameters are not used?
// 12/19/2018: Probably not - it can make sense to have an API with
// all functions uniformly sharing the same type parameters.
// spec: "Implementation restriction: A compiler may make it illegal to
// declare a variable inside a function body if the variable is never used."
check.usage(sig.scope)
@ -147,9 +152,9 @@ func (check *Checker) multipleDefaults(list []ast.Stmt) {
}
}
func (check *Checker) openScope(s ast.Node, comment string) {
scope := NewScope(check.scope, s.Pos(), s.End(), comment)
check.recordScope(s, scope)
func (check *Checker) openScope(node ast.Node, comment string) {
scope := NewScope(check.scope, node.Pos(), node.End(), comment)
check.recordScope(node, scope)
check.scope = scope
}
@ -273,6 +278,9 @@ L:
if T == Typ[Invalid] {
continue L
}
if T != nil {
check.ordinaryType(e, T)
}
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
for t, other := range seen {
@ -355,8 +363,8 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
return
}
tch, ok := ch.typ.Underlying().(*Chan)
if !ok {
tch := asChan(ch.typ)
if tch == nil {
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
return
}
@ -609,7 +617,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
return
}
// rhs must be of the form: expr.(type) and expr must be an interface
// rhs must be of the form: expr.(type) and expr must be an ordinary interface
expr, _ := rhs.(*ast.TypeAssertExpr)
if expr == nil || expr.Type != nil {
check.invalidAST(s, "incorrect form of type switch guard")
@ -620,11 +628,12 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if x.mode == invalid {
return
}
xtyp, _ := x.typ.Underlying().(*Interface)
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
return
}
check.ordinaryType(&x, xtyp)
check.multipleDefaults(s.Body.List)
@ -761,43 +770,22 @@ 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]
if typ.dir == SendOnly {
check.errorf(&x, _InvalidChanRange, "cannot range over send-only channel %s", &x)
// ok to continue
}
if s.Value != nil {
check.errorf(atPos(s.Value.Pos()), _InvalidIterVar, "iteration over %s permits only one iteration variable", &x)
// ok to continue
}
typ := optype(x.typ)
if _, ok := typ.(*Chan); ok && s.Value != nil {
// TODO(gri) this also needs to happen for channels in generic variables
check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
// ok to continue
}
var msg string
key, val, msg = rangeKeyVal(typ, isVarName(s.Key), isVarName(s.Value))
if key == nil || msg != "" {
if msg != "" {
// TODO(rFindley) should this be parenthesized, to be consistent with other qualifiers?
msg = ": " + msg
}
check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg)
// ok to continue
}
}
if key == nil {
check.errorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
// ok to continue
}
// check assignment to/declaration of iteration variables
@ -879,3 +867,72 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.invalidAST(s, "invalid statement")
}
}
// isVarName reports whether x is a non-nil, non-blank (_) expression.
func isVarName(x ast.Expr) bool {
if x == nil {
return false
}
ident, _ := unparen(x).(*ast.Ident)
return ident == nil || ident.Name != "_"
}
// rangeKeyVal returns the key and value type produced by a range clause
// over an expression of type typ, and possibly an error message. If the
// range clause is not permitted the returned key is nil or msg is not
// empty (in that case we still may have a non-nil key type which can be
// used to reduce the chance for follow-on errors).
// The wantKey, wantVal, and hasVal flags indicate which of the iteration
// variables are used or present; this matters if we range over a generic
// type where not all keys or values are of the same type.
func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
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 := asArray(typ.base); typ != nil {
return Typ[Int], typ.elem, ""
}
case *Map:
return typ.key, typ.elem, ""
case *Chan:
var msg string
if typ.dir == SendOnly {
msg = "send-only channel"
}
return typ.elem, Typ[Invalid], msg
case *Sum:
first := true
var key, val Type
var msg string
typ.is(func(t Type) bool {
k, v, m := rangeKeyVal(under(t), wantKey, wantVal)
if k == nil || m != "" {
key, val, msg = k, v, m
return false
}
if first {
key, val, msg = k, v, m
first = false
return true
}
if wantKey && !Identical(key, k) {
key, val, msg = nil, nil, "all possible values must have the same key type"
return false
}
if wantVal && !Identical(val, v) {
key, val, msg = nil, nil, "all possible values must have the same element type"
return false
}
return true
})
return key, val, msg
}
return nil, nil, ""
}

View File

@ -886,7 +886,7 @@ func rangeloops1() {
ee = e
_ = ee
}
for _ = range sc /* ERROR "cannot range over send-only channel" */ {}
for _ = range sc /* ERROR "cannot range over" */ {}
for _ = range rc {}
// constant strings