summaryrefslogtreecommitdiff
path: root/libgo/go/go/types/stmt.go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 14:53:56 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:01:19 -0800
commit8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/go/types/stmt.go
parent9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff)
downloadgcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz
libgo: update to Go1.18beta2
gotools/ * Makefile.am (go_cmd_cgo_files): Add ast_go118.go (check-go-tool): Copy golang.org/x/tools directories. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/384695
Diffstat (limited to 'libgo/go/go/types/stmt.go')
-rw-r--r--libgo/go/go/types/stmt.go285
1 files changed, 150 insertions, 135 deletions
diff --git a/libgo/go/go/types/stmt.go b/libgo/go/go/types/stmt.go
index 47f6dcfbd1c..802673567d2 100644
--- a/libgo/go/go/types/stmt.go
+++ b/libgo/go/go/types/stmt.go
@@ -15,7 +15,7 @@ import (
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt, iota constant.Value) {
if check.conf.IgnoreFuncBodies {
- panic("internal error: function body not ignored")
+ panic("function body not ignored")
}
if trace {
@@ -29,13 +29,13 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
sig.scope.pos = body.Pos()
sig.scope.end = body.End()
- // save/restore current context and setup function context
+ // save/restore current environment and set up function environment
// (and use 0 indentation at function start)
- defer func(ctxt context, indent int) {
- check.context = ctxt
+ defer func(env environment, indent int) {
+ check.environment = env
check.indent = indent
- }(check.context, check.indent)
- check.context = context{
+ }(check.environment, check.indent)
+ check.environment = environment{
decl: decl,
scope: sig.scope,
iota: iota,
@@ -53,11 +53,6 @@ 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)
@@ -65,7 +60,8 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
func (check *Checker) usage(scope *Scope) {
var unused []*Var
- for _, elem := range scope.elems {
+ for name, elem := range scope.elems {
+ elem = resolve(name, elem)
if v, _ := elem.(*Var); v != nil && !v.used {
unused = append(unused, v)
}
@@ -178,7 +174,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
var x operand
var msg string
var code errorCode
- switch check.rawExpr(&x, call, nil) {
+ switch check.rawExpr(&x, call, nil, false) {
case conversion:
msg = "requires function call, not conversion"
code = _InvalidDefer
@@ -197,7 +193,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
}
// goVal returns the Go value for val, or nil.
-func goVal(val constant.Value) interface{} {
+func goVal(val constant.Value) any {
// val should exist, but be conservative and check
if val == nil {
return nil
@@ -231,7 +227,7 @@ func goVal(val constant.Value) interface{} {
// types we need to also check the value's types (e.g., byte(1) vs myByte(1))
// when the switch expression is of interface type.
type (
- valueMap map[interface{}][]valueType // underlying Go value -> valueType
+ valueMap map[any][]valueType // underlying Go value -> valueType
valueType struct {
pos token.Pos
typ Type
@@ -264,7 +260,7 @@ L:
// look for duplicate types for a given value
// (quadratic algorithm, but these lists tend to be very short)
for _, vt := range seen[val] {
- if check.identical(v.typ, vt.typ) {
+ if Identical(v.typ, vt.typ) {
check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v)
check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case") // secondary error, \t indented
continue L
@@ -275,24 +271,38 @@ L:
}
}
+// isNil reports whether the expression e denotes the predeclared value nil.
+func (check *Checker) isNil(e ast.Expr) bool {
+ // The only way to express the nil value is by literally writing nil (possibly in parentheses).
+ if name, _ := unparen(e).(*ast.Ident); name != nil {
+ _, ok := check.lookup(name.Name).(*Nil)
+ return ok
+ }
+ return false
+}
+
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
+ var dummy operand
L:
for _, e := range types {
- T = check.typeOrNil(e)
- if T == Typ[Invalid] {
- continue L
- }
- if T != nil {
- check.ordinaryType(e, T)
+ // The spec allows the value nil instead of a type.
+ if check.isNil(e) {
+ T = nil
+ check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
+ } else {
+ T = check.varType(e)
+ if T == Typ[Invalid] {
+ continue L
+ }
}
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
for t, other := range seen {
- if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
+ if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
- Ts = T.String()
+ Ts = TypeString(T, check.qualifier)
}
check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
@@ -300,13 +310,54 @@ L:
}
}
seen[T] = e
- if T != nil {
+ if T != nil && xtyp != nil {
check.typeAssertion(e, x, xtyp, T)
}
}
return
}
+// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead.
+// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.)
+//
+// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) {
+// var dummy operand
+// L:
+// for _, e := range types {
+// // The spec allows the value nil instead of a type.
+// var hash string
+// if check.isNil(e) {
+// check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
+// T = nil
+// hash = "<nil>" // avoid collision with a type named nil
+// } else {
+// T = check.varType(e)
+// if T == Typ[Invalid] {
+// continue L
+// }
+// hash = typeHash(T, nil)
+// }
+// // look for duplicate types
+// if other := seen[hash]; other != nil {
+// // talk about "case" rather than "type" because of nil case
+// Ts := "nil"
+// if T != nil {
+// Ts = TypeString(T, check.qualifier)
+// }
+// var err error_
+// err.errorf(e, "duplicate case %s in type switch", Ts)
+// err.errorf(other, "previous case")
+// check.report(&err)
+// continue L
+// }
+// seen[hash] = e
+// if T != nil {
+// check.typeAssertion(e.Pos(), x, xtyp, T)
+// }
+// }
+// return
+// }
+
// stmt typechecks statement s.
func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// statements must end with the same top scope as they started with
@@ -340,7 +391,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// function and method calls and receive operations can appear
// in statement context. Such statements may be parenthesized."
var x operand
- kind := check.rawExpr(&x, s.X, nil)
+ kind := check.rawExpr(&x, s.X, nil, false)
var msg string
var code errorCode
switch x.mode {
@@ -360,25 +411,27 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.errorf(&x, code, "%s %s", &x, msg)
case *ast.SendStmt:
- var ch, x operand
+ var ch, val operand
check.expr(&ch, s.Chan)
- check.expr(&x, s.Value)
- if ch.mode == invalid || x.mode == invalid {
+ check.expr(&val, s.Value)
+ if ch.mode == invalid || val.mode == invalid {
return
}
-
- tch := asChan(ch.typ)
- if tch == nil {
- check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
+ u := structuralType(ch.typ)
+ if u == nil {
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no structural type", &ch)
return
}
-
- if tch.dir == RecvOnly {
- check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only type %s", tch)
+ uch, _ := u.(*Chan)
+ if uch == nil {
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch)
return
}
-
- check.assignment(&x, tch.elem, "send")
+ if uch.dir == RecvOnly {
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch)
+ return
+ }
+ check.assignment(&val, uch.elem, "send")
case *ast.IncDecStmt:
var op token.Token
@@ -397,7 +450,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if x.mode == invalid {
return
}
- if !isNumeric(x.typ) {
+ if !allNumeric(x.typ) {
check.invalidOp(s.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
return
}
@@ -450,27 +503,25 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
case *ast.ReturnStmt:
res := check.sig.results
- if res.Len() > 0 {
- // function returns results
- // (if one, say the first, result parameter is named, all of them are named)
- if len(s.Results) == 0 && res.vars[0].name != "" {
- // spec: "Implementation restriction: A compiler may disallow an empty expression
- // list in a "return" statement if a different entity (constant, type, or variable)
- // with the same name as a result parameter is in scope at the place of the return."
- for _, obj := range res.vars {
- if alt := check.lookup(obj.name); alt != nil && alt != obj {
- check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
- check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
- // ok to continue
- }
+ // Return with implicit results allowed for function with named results.
+ // (If one is named, all are named.)
+ if len(s.Results) == 0 && res.Len() > 0 && res.vars[0].name != "" {
+ // spec: "Implementation restriction: A compiler may disallow an empty expression
+ // list in a "return" statement if a different entity (constant, type, or variable)
+ // with the same name as a result parameter is in scope at the place of the return."
+ for _, obj := range res.vars {
+ if alt := check.lookup(obj.name); alt != nil && alt != obj {
+ check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
+ check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
+ // ok to continue
}
- } else {
- // return has results or result parameters are unnamed
- check.initVars(res.vars, s.Results, s.Return)
}
- } else if len(s.Results) > 0 {
- check.error(s.Results[0], _WrongResultCount, "no result values expected")
- check.use(s.Results...)
+ } else {
+ var lhs []*Var
+ if res.Len() > 0 {
+ lhs = res.vars
+ }
+ check.initVars(lhs, s.Results, s)
}
case *ast.BranchStmt:
@@ -513,7 +564,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.simpleStmt(s.Init)
var x operand
check.expr(&x, s.Cond)
- if x.mode != invalid && !isBoolean(x.typ) {
+ if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, _InvalidCond, "non-boolean condition in if statement")
}
check.stmt(inner, s.Body)
@@ -632,12 +683,16 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if x.mode == invalid {
return
}
- xtyp, _ := under(x.typ).(*Interface)
- if xtyp == nil {
- check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
- return
+ // TODO(gri) we may want to permit type switches on type parameter values at some point
+ var xtyp *Interface
+ if isTypeParam(x.typ) {
+ check.errorf(&x, _InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
+ } else {
+ xtyp, _ = under(x.typ).(*Interface)
+ if xtyp == nil {
+ check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
+ }
}
- check.ordinaryType(&x, xtyp)
check.multipleDefaults(s.Body.List)
@@ -746,7 +801,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if s.Cond != nil {
var x operand
check.expr(&x, s.Cond)
- if x.mode != invalid && !isBoolean(x.typ) {
+ if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, _InvalidCond, "non-boolean condition in for statement")
}
}
@@ -774,20 +829,28 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// determine key/value types
var key, val Type
if x.mode != invalid {
- 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
+ // Ranging over a type parameter is permitted if it has a structural type.
+ var cause string
+ u := structuralType(x.typ)
+ switch t := u.(type) {
+ case nil:
+ cause = check.sprintf("%s has no structural type", x.typ)
+ case *Chan:
+ if s.Value != nil {
+ check.softErrorf(s.Value, _InvalidIterVar, "range over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ if t.dir == SendOnly {
+ cause = "receive from send-only channel"
+ }
}
- 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
+ key, val = rangeKeyVal(u)
+ if key == nil || cause != "" {
+ if cause == "" {
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
+ } else {
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s (%s)", &x, cause)
}
- check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg)
// ok to continue
}
}
@@ -872,71 +935,23 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
}
}
-// 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) {
+// over an expression of type typ. If the range clause is not permitted
+// the results are nil.
+func rangeKeyVal(typ Type) (key, val Type) {
+ switch typ := arrayPtrDeref(typ).(type) {
case *Basic:
if isString(typ) {
- return Typ[Int], universeRune, "" // use 'rune' name
+ return Typ[Int], universeRune // use 'rune' name
}
case *Array:
- return Typ[Int], typ.elem, ""
+ 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, ""
- }
+ return Typ[Int], typ.elem
case *Map:
- return typ.key, typ.elem, ""
+ 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 typ.elem, Typ[Invalid]
}
- return nil, nil, ""
+ return
}