diff options
author | Ian Lance Taylor <iant@golang.org> | 2022-02-11 14:53:56 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2022-02-11 15:01:19 -0800 |
commit | 8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch) | |
tree | 43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/go/types/stmt.go | |
parent | 9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff) | |
download | gcc-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.go | 285 |
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 } |