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)
}
}
-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
}
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 {
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
}
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")
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)
// 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)
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, ""
+}