]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] import stmt changes from dev.go2go
authorRob Findley <rfindley@google.com>
Thu, 7 Jan 2021 16:13:56 +0000 (11:13 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 11 Jan 2021 18:23:13 +0000 (18:23 +0000)
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>

src/go/types/errorcodes.go
src/go/types/stmt.go
src/go/types/testdata/stmt0.src

index 897b34d74f54a1bdae25492028ac83088b8b568a..2c5a291660a0613a9b77cfabf4a5208219edec16 100644 (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.
        //
index 0162368a64335c7877eb2ef99c15d39194175234..82c21c2a7a62ae0fa3de00d6312d2a253a14ba43 100644 (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,45 +770,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]
-                               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
                // (irregular assignment, cannot easily map to existing assignment checks)
 
@@ -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, ""
+}
index fde846962e9934f8183cdf508c47027454b4a50a..297e34be421ccc26a9d31c174142da0cf8ea0cad 100644 (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