mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00: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
3 changed files with 101 additions and 56 deletions
|
|
@ -1038,18 +1038,6 @@ const (
|
||||||
// }
|
// }
|
||||||
_InvalidPostDecl
|
_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
|
// _InvalidIterVar occurs when two iteration variables are used while ranging
|
||||||
// over a channel.
|
// 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")
|
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
|
// spec: "Implementation restriction: A compiler may make it illegal to
|
||||||
// declare a variable inside a function body if the variable is never used."
|
// declare a variable inside a function body if the variable is never used."
|
||||||
check.usage(sig.scope)
|
check.usage(sig.scope)
|
||||||
|
|
@ -147,9 +152,9 @@ func (check *Checker) multipleDefaults(list []ast.Stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) openScope(s ast.Node, comment string) {
|
func (check *Checker) openScope(node ast.Node, comment string) {
|
||||||
scope := NewScope(check.scope, s.Pos(), s.End(), comment)
|
scope := NewScope(check.scope, node.Pos(), node.End(), comment)
|
||||||
check.recordScope(s, scope)
|
check.recordScope(node, scope)
|
||||||
check.scope = scope
|
check.scope = scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,6 +278,9 @@ L:
|
||||||
if T == Typ[Invalid] {
|
if T == Typ[Invalid] {
|
||||||
continue L
|
continue L
|
||||||
}
|
}
|
||||||
|
if T != nil {
|
||||||
|
check.ordinaryType(e, T)
|
||||||
|
}
|
||||||
// look for duplicate types
|
// look for duplicate types
|
||||||
// (quadratic algorithm, but type switches tend to be reasonably small)
|
// (quadratic algorithm, but type switches tend to be reasonably small)
|
||||||
for t, other := range seen {
|
for t, other := range seen {
|
||||||
|
|
@ -355,8 +363,8 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tch, ok := ch.typ.Underlying().(*Chan)
|
tch := asChan(ch.typ)
|
||||||
if !ok {
|
if tch == nil {
|
||||||
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
|
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +617,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
return
|
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)
|
expr, _ := rhs.(*ast.TypeAssertExpr)
|
||||||
if expr == nil || expr.Type != nil {
|
if expr == nil || expr.Type != nil {
|
||||||
check.invalidAST(s, "incorrect form of type switch guard")
|
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 {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
xtyp, _ := x.typ.Underlying().(*Interface)
|
xtyp, _ := under(x.typ).(*Interface)
|
||||||
if xtyp == nil {
|
if xtyp == nil {
|
||||||
check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
|
check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
check.ordinaryType(&x, xtyp)
|
||||||
|
|
||||||
check.multipleDefaults(s.Body.List)
|
check.multipleDefaults(s.Body.List)
|
||||||
|
|
||||||
|
|
@ -761,43 +770,22 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
// determine key/value types
|
// determine key/value types
|
||||||
var key, val Type
|
var key, val Type
|
||||||
if x.mode != invalid {
|
if x.mode != invalid {
|
||||||
switch typ := x.typ.Underlying().(type) {
|
typ := optype(x.typ)
|
||||||
case *Basic:
|
if _, ok := typ.(*Chan); ok && s.Value != nil {
|
||||||
if isString(typ) {
|
// TODO(gri) this also needs to happen for channels in generic variables
|
||||||
key = Typ[Int]
|
check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
|
||||||
val = universeRune // use 'rune' name
|
// ok to continue
|
||||||
}
|
}
|
||||||
case *Array:
|
var msg string
|
||||||
key = Typ[Int]
|
key, val, msg = rangeKeyVal(typ, isVarName(s.Key), isVarName(s.Value))
|
||||||
val = typ.elem
|
if key == nil || msg != "" {
|
||||||
case *Slice:
|
if msg != "" {
|
||||||
key = Typ[Int]
|
// TODO(rFindley) should this be parenthesized, to be consistent with other qualifiers?
|
||||||
val = typ.elem
|
msg = ": " + msg
|
||||||
case *Pointer:
|
}
|
||||||
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
|
check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg)
|
||||||
key = Typ[Int]
|
// ok to continue
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if key == nil {
|
|
||||||
check.errorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
|
|
||||||
// ok to continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check assignment to/declaration of iteration variables
|
// 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")
|
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 = e
|
||||||
_ = ee
|
_ = ee
|
||||||
}
|
}
|
||||||
for _ = range sc /* ERROR "cannot range over send-only channel" */ {}
|
for _ = range sc /* ERROR "cannot range over" */ {}
|
||||||
for _ = range rc {}
|
for _ = range rc {}
|
||||||
|
|
||||||
// constant strings
|
// constant strings
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue