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:
parent
eb53a6c7cf
commit
1ce0854157
@ -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.
|
||||
//
|
||||
|
@ -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, ""
|
||||
}
|
||||
|
2
src/go/types/testdata/stmt0.src
vendored
2
src/go/types/testdata/stmt0.src
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user