diff options
Diffstat (limited to 'src/go/types')
-rw-r--r-- | src/go/types/api.go | 6 | ||||
-rw-r--r-- | src/go/types/assignments.go | 10 | ||||
-rw-r--r-- | src/go/types/call.go | 9 | ||||
-rw-r--r-- | src/go/types/conversions.go | 5 | ||||
-rw-r--r-- | src/go/types/errors.go | 140 | ||||
-rw-r--r-- | src/go/types/errors_test.go | 25 | ||||
-rw-r--r-- | src/go/types/expr.go | 12 | ||||
-rw-r--r-- | src/go/types/sizes.go | 7 | ||||
-rw-r--r-- | src/go/types/testdata/check/const0.go | 19 | ||||
-rw-r--r-- | src/go/types/testdata/fixedbugs/issue52401.go | 11 |
10 files changed, 177 insertions, 67 deletions
diff --git a/src/go/types/api.go b/src/go/types/api.go index 4f63d627130..0915d6a6eee 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -414,9 +414,9 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i // AssertableTo reports whether a value of type V can be asserted to have type T. // // The behavior of AssertableTo is undefined in two cases: -// - if V is a generalized interface; i.e., an interface that may only be used -// as a type constraint in Go code -// - if T is an uninstantiated generic type +// - if V is a generalized interface; i.e., an interface that may only be used +// as a type constraint in Go code +// - if T is an uninstantiated generic type func AssertableTo(V *Interface, T Type) bool { // Checker.newAssertableTo suppresses errors for invalid types, so we need special // handling here. diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index 101e868d82f..98d75630efe 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -9,6 +9,7 @@ package types import ( "fmt" "go/ast" + "go/token" "strings" ) @@ -339,11 +340,10 @@ func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.St } else if len(rhs) > 0 { at = rhs[len(rhs)-1].expr // report at last value } - check.errorf(at, _WrongResultCount, "%s return values\n\thave %s\n\twant %s", - qualifier, - check.typesSummary(operandTypes(rhs), false), - check.typesSummary(varTypes(lhs), false), - ) + err := newErrorf(at, _WrongResultCount, "%s return values", qualifier) + err.errorf(token.NoPos, "have %s", check.typesSummary(operandTypes(rhs), false)) + err.errorf(token.NoPos, "want %s", check.typesSummary(varTypes(lhs), false)) + check.report(err) return } if compilerErrorMessages { diff --git a/src/go/types/call.go b/src/go/types/call.go index 51603170a60..3c7c3226f62 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -368,11 +368,10 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type if sig.params != nil { params = sig.params.vars } - check.errorf(at, _WrongArgCount, "%s arguments in call to %s\n\thave %s\n\twant %s", - qualifier, call.Fun, - check.typesSummary(operandTypes(args), false), - check.typesSummary(varTypes(params), sig.variadic), - ) + err := newErrorf(at, _WrongArgCount, "%s arguments in call to %s", qualifier, call.Fun) + err.errorf(token.NoPos, "have %s", check.typesSummary(operandTypes(args), false)) + err.errorf(token.NoPos, "want %s", check.typesSummary(varTypes(params), sig.variadic)) + check.report(err) return } diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index 65691cf4550..362c8fdbac9 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -8,6 +8,7 @@ package types import ( "go/constant" + "go/token" "unicode" ) @@ -74,7 +75,9 @@ func (check *Checker) conversion(x *operand, T Type) { if compilerErrorMessages { if cause != "" { // Add colon at end of line if we have a following cause. - check.errorf(x, _InvalidConversion, "cannot convert %s to type %s:\n\t%s", x, T, cause) + err := newErrorf(x, _InvalidConversion, "cannot convert %s to type %s:", x, T) + err.errorf(token.NoPos, cause) + check.report(err) } else { check.errorf(x, _InvalidConversion, "cannot convert %s to type %s", x, T) } diff --git a/src/go/types/errors.go b/src/go/types/errors.go index fade8630e09..0dc0bc87991 100644 --- a/src/go/types/errors.go +++ b/src/go/types/errors.go @@ -8,7 +8,6 @@ package types import ( "bytes" - "errors" "fmt" "go/ast" "go/token" @@ -26,6 +25,64 @@ func unreachable() { panic("unreachable") } +// An error_ represents a type-checking error. +// To report an error_, call Checker.report. +type error_ struct { + desc []errorDesc + code errorCode + soft bool // TODO(gri) eventually determine this from an error code +} + +// An errorDesc describes part of a type-checking error. +type errorDesc struct { + posn positioner + format string + args []interface{} +} + +func (err *error_) empty() bool { + return err.desc == nil +} + +func (err *error_) pos() token.Pos { + if err.empty() { + return token.NoPos + } + return err.desc[0].posn.Pos() +} + +func (err *error_) msg(fset *token.FileSet, qf Qualifier) string { + if err.empty() { + return "no error" + } + var buf bytes.Buffer + for i := range err.desc { + p := &err.desc[i] + if i > 0 { + fmt.Fprint(&buf, "\n\t") + if p.posn.Pos().IsValid() { + fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos())) + } + } + buf.WriteString(sprintf(fset, qf, false, p.format, p.args...)) + } + return buf.String() +} + +// String is for testing. +func (err *error_) String() string { + if err.empty() { + return "no error" + } + return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil)) +} + +// errorf adds formatted error information to err. +// It may be called multiple times to provide additional information. +func (err *error_) errorf(at token.Pos, format string, args ...interface{}) { + err.desc = append(err.desc, errorDesc{atPos(at), format, args}) +} + func (check *Checker) qualifier(pkg *Package) string { // Qualify the package unless it's the package being type-checked. if pkg != check.pkg { @@ -140,36 +197,46 @@ func (check *Checker) dump(format string, args ...any) { fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...)) } -func (check *Checker) err(err error) { - if err == nil { - return +// Report records the error pointed to by errp, setting check.firstError if +// necessary. +func (check *Checker) report(errp *error_) { + if errp.empty() { + panic("empty error details") + } + + span := spanOf(errp.desc[0].posn) + e := Error{ + Fset: check.fset, + Pos: span.pos, + Msg: errp.msg(check.fset, check.qualifier), + Soft: errp.soft, + go116code: errp.code, + go116start: span.start, + go116end: span.end, } - var e Error - isInternal := errors.As(err, &e) + // Cheap trick: Don't report errors with messages containing // "invalid operand" or "invalid type" as those tend to be // follow-on errors which don't add useful information. Only // exclude them if these strings are not at the beginning, // and only if we have at least one error already reported. - isInvalidErr := isInternal && (strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0) + isInvalidErr := strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0 if check.firstErr != nil && isInvalidErr { return } - if isInternal { - e.Msg = stripAnnotations(e.Msg) - if check.errpos != nil { - // If we have an internal error and the errpos override is set, use it to - // augment our error positioning. - // TODO(rFindley) we may also want to augment the error message and refer - // to the position (pos) in the original expression. - span := spanOf(check.errpos) - e.Pos = span.pos - e.go116start = span.start - e.go116end = span.end - } - err = e + e.Msg = stripAnnotations(e.Msg) + if check.errpos != nil { + // If we have an internal error and the errpos override is set, use it to + // augment our error positioning. + // TODO(rFindley) we may also want to augment the error message and refer + // to the position (pos) in the original expression. + span := spanOf(check.errpos) + e.Pos = span.pos + e.go116start = span.start + e.go116end = span.end } + err := e if check.firstErr == nil { check.firstErr = err @@ -178,10 +245,6 @@ func (check *Checker) err(err error) { if trace { pos := e.Pos msg := e.Msg - if !isInternal { - msg = err.Error() - pos = token.NoPos - } check.trace(pos, "ERROR: %s", msg) } @@ -192,35 +255,26 @@ func (check *Checker) err(err error) { f(err) } -func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error { - span := spanOf(at) - return Error{ - Fset: check.fset, - Pos: span.pos, - Msg: msg, - Soft: soft, - go116code: code, - go116start: span.start, - go116end: span.end, +// newErrorf creates a new error_ for later reporting with check.report. +func newErrorf(at positioner, code errorCode, format string, args ...any) *error_ { + return &error_{ + desc: []errorDesc{{at, format, args}}, + code: code, } } -// newErrorf creates a new Error, but does not handle it. -func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...any) error { - msg := check.sprintf(format, args...) - return check.newError(at, code, soft, msg) -} - func (check *Checker) error(at positioner, code errorCode, msg string) { - check.err(check.newError(at, code, false, msg)) + check.report(newErrorf(at, code, msg)) } func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) { - check.error(at, code, check.sprintf(format, args...)) + check.report(newErrorf(at, code, format, args...)) } func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) { - check.err(check.newErrorf(at, code, true, format, args...)) + err := newErrorf(at, code, format, args...) + err.soft = true + check.report(err) } func (check *Checker) invalidAST(at positioner, format string, args ...any) { diff --git a/src/go/types/errors_test.go b/src/go/types/errors_test.go index 942a9fdd4c2..4b5dab68e4e 100644 --- a/src/go/types/errors_test.go +++ b/src/go/types/errors_test.go @@ -4,7 +4,30 @@ package types -import "testing" +import ( + "go/token" + "testing" +) + +func TestError(t *testing.T) { + var err error_ + want := "no error" + if got := err.String(); got != want { + t.Errorf("empty error: got %q, want %q", got, want) + } + + want = "0: foo 42" + err.errorf(token.NoPos, "foo %d", 42) + if got := err.String(); got != want { + t.Errorf("simple error: got %q, want %q", got, want) + } + + want = "0: foo 42\n\tbar 43" + err.errorf(token.NoPos, "bar %d", 43) + if got := err.String(); got != want { + t.Errorf("simple error: got %q, want %q", got, want) + } +} func TestStripAnnotations(t *testing.T) { for _, test := range []struct { diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 977153512f9..70914d54852 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -87,7 +87,7 @@ func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool { // overflow checks that the constant x is representable by its type. // For untyped constants, it checks that the value doesn't become // arbitrarily large. -func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) { +func (check *Checker) overflow(x *operand, opPos token.Pos) { assert(x.mode == constant_) if x.val.Kind() == constant.Unknown { @@ -115,8 +115,8 @@ func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) { } } -// opName returns the name of an operation, or the empty string. -// Only operations that might overflow are handled. +// opName returns the name of the operation if x is an operation +// that might overflow; otherwise it returns the empty string. func opName(e ast.Expr) string { switch e := e.(type) { case *ast.BinaryExpr: @@ -213,7 +213,7 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { } x.val = constant.UnaryOp(e.Op, x.val, prec) x.expr = e - check.overflow(x, e.Op, x.Pos()) + check.overflow(x, x.Pos()) return } @@ -991,7 +991,7 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) { if b, _ := e.(*ast.BinaryExpr); b != nil { opPos = b.OpPos } - check.overflow(x, op, opPos) + check.overflow(x, opPos) return } @@ -1171,7 +1171,7 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token } x.val = constant.BinaryOp(x.val, op, y.val) x.expr = e - check.overflow(x, op, opPos) + check.overflow(x, opPos) return } diff --git a/src/go/types/sizes.go b/src/go/types/sizes.go index 494e0454770..7b67dca2b8b 100644 --- a/src/go/types/sizes.go +++ b/src/go/types/sizes.go @@ -166,10 +166,11 @@ func (s *StdSizes) Sizeof(T Type) int64 { // common architecture word sizes and alignments var gcArchSizes = map[string]*StdSizes{ "386": {4, 4}, - "arm": {4, 4}, - "arm64": {8, 8}, "amd64": {8, 8}, "amd64p32": {4, 8}, + "arm": {4, 4}, + "arm64": {8, 8}, + "loong64": {8, 8}, "mips": {4, 4}, "mipsle": {4, 4}, "mips64": {8, 8}, @@ -188,7 +189,7 @@ var gcArchSizes = map[string]*StdSizes{ // The result is nil if a compiler/architecture pair is not known. // // Supported architectures for compiler "gc": -// "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle", +// "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle", // "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm". func SizesFor(compiler, arch string) Sizes { var m map[string]*StdSizes diff --git a/src/go/types/testdata/check/const0.go b/src/go/types/testdata/check/const0.go index 3cffdf904c8..229c248643d 100644 --- a/src/go/types/testdata/check/const0.go +++ b/src/go/types/testdata/check/const0.go @@ -349,6 +349,25 @@ const _ = unsafe.Sizeof(func() { assert(iota == 0) }) +// issue #52438 +const i1 = iota +const i2 = iota +const i3 = iota + +func _() { + assert(i1 == 0) + assert(i2 == 0) + assert(i3 == 0) + + const i4 = iota + const i5 = iota + const i6 = iota + + assert(i4 == 0) + assert(i5 == 0) + assert(i6 == 0) +} + // untyped constants must not get arbitrarily large const prec = 512 // internal maximum precision for integers const maxInt = (1<<(prec/2) - 1) * (1<<(prec/2) + 1) // == 1<<prec - 1 diff --git a/src/go/types/testdata/fixedbugs/issue52401.go b/src/go/types/testdata/fixedbugs/issue52401.go new file mode 100644 index 00000000000..c7efd8c7180 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue52401.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _() { + const x = 0 + x /* ERROR cannot assign to x */ += 1 + x /* ERROR cannot assign to x */ ++ +} |