func testdclstack() {
if !types.IsDclstackValid() {
- if nerrors != 0 {
- errorexit()
- }
Fatalf("mark left on the dclstack")
}
}
var outfile string
var linkobj string
-// nerrors is the number of compiler errors reported
-// since the last call to saveerrors.
-var nerrors int
-
-// nsavederrors is the total number of compiler errors
-// reported before the last call to saveerrors.
-var nsavederrors int
-
-var nsyntaxerrors int
-
var decldepth int32
var nolocalimports bool
// confused us and there might not be
// a loop. Let the user fix those
// first.
- if nerrors > 0 {
- errorexit()
- }
+ ExitIfErrors()
findInitLoopAndExit(firstLHS(n), new([]*Node))
Fatalf("initialization unfinished, but failed to identify loop")
"strings"
)
-// lineno is the source position at the start of the most recently lexed token.
-// TODO(gri) rename and eventually remove
-var lineno src.XPos
-
func makePos(base *src.PosBase, line, col uint) src.XPos {
return Ctxt.PosTable.XPos(src.MakePos(base, line, col))
}
}
func hidePanic() {
- if Debug_panic == 0 && nsavederrors+nerrors > 0 {
+ if Debug_panic == 0 && Errors() > 0 {
// If we've already complained about things
// in the program, don't bother complaining
// about a panic too; let the user clean up
initUniverse()
dclcontext = PEXTERN
- nerrors = 0
autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
if n.Op == ODCLFUNC {
Curfn = n
decldepth = 1
- saveerrors()
+ errorsBefore := Errors()
typecheckslice(Curfn.Nbody.Slice(), ctxStmt)
checkreturn(Curfn)
- if nerrors != 0 {
+ if Errors() > errorsBefore {
Curfn.Nbody.Set(nil) // type errors; do not compile
}
// Now that we've checked whether n terminates,
// check past phase 9 isn't sufficient, as we may exit with other errors
// before then, thus skipping map key errors.
checkMapKeys()
- timings.AddEvent(fcount, "funcs")
+ ExitIfErrors()
- if nsavederrors+nerrors != 0 {
- errorexit()
- }
+ timings.AddEvent(fcount, "funcs")
fninit(xtop)
}
}
capturevarscomplete = true
-
Curfn = nil
-
- if nsavederrors+nerrors != 0 {
- errorexit()
- }
+ ExitIfErrors()
// Phase 5: Inlining
timings.Start("fe", "inlining")
// otherwise lazily when used or re-exported.
for _, n := range importlist {
if n.Func.Inl != nil {
- saveerrors()
typecheckinl(n)
}
}
-
- if nsavederrors+nerrors != 0 {
- errorexit()
- }
+ ExitIfErrors()
}
if Debug.l != 0 {
// Check the map keys again, since we typechecked the external
// declarations.
checkMapKeys()
-
- if nerrors+nsavederrors != 0 {
- errorexit()
- }
+ ExitIfErrors()
// Write object data to disk.
timings.Start("be", "dumpobj")
}
logopt.FlushLoggedOpts(Ctxt, myimportpath)
-
- if nerrors+nsavederrors != 0 {
- errorexit()
- }
+ ExitIfErrors()
flusherrors()
timings.Stop()
}
}
-func saveerrors() {
- nsavederrors += nerrors
- nerrors = 0
-}
-
func arsize(b *bufio.Reader, name string) int {
var buf [ArhdrSize]byte
if _, err := io.ReadFull(b, buf[:]); err != nil {
// leave s->block set to cause redeclaration
// errors if a conflicting top-level name is
// introduced by a different file.
- if !n.Name.Used() && nsyntaxerrors == 0 {
+ if !n.Name.Used() && SyntaxErrors() == 0 {
unused = append(unused, importedPkg{n.Pos, n.Name.Pkg.Path, s.Name})
}
s.Def = nil
if IsAlias(s) {
// throw away top-level name left over
// from previous import . "x"
- if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && nsyntaxerrors == 0 {
+ if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && SyntaxErrors() == 0 {
unused = append(unused, importedPkg{n.Name.Pack.Pos, n.Name.Pack.Name.Pkg.Path, ""})
n.Name.Pack.Name.SetUsed(true)
}
x, _ := a.Val.Float64()
// check for overflow
- if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
+ if math.IsInf(x, 0) && Errors() == 0 {
Fatalf("ovf in Mpflt Float64")
}
x := float64(x32)
// check for overflow
- if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
+ if math.IsInf(x, 0) && Errors() == 0 {
Fatalf("ovf in Mpflt Float32")
}
func (a *Mpint) Add(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Add")
}
a.SetOverflow()
func (a *Mpint) Sub(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Sub")
}
a.SetOverflow()
func (a *Mpint) Mul(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Mul")
}
a.SetOverflow()
func (a *Mpint) Quo(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Quo")
}
a.SetOverflow()
func (a *Mpint) Rem(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Rem")
}
a.SetOverflow()
func (a *Mpint) Or(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Or")
}
a.SetOverflow()
func (a *Mpint) And(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint And")
}
a.SetOverflow()
func (a *Mpint) AndNot(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint AndNot")
}
a.SetOverflow()
func (a *Mpint) Xor(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Xor")
}
a.SetOverflow()
func (a *Mpint) Lsh(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Lsh")
}
a.SetOverflow()
func (a *Mpint) Rsh(b *Mpint) {
if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("ovf in Mpint Rsh")
}
a.SetOverflow()
func (a *Mpint) Int64() int64 {
if a.Ovf {
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
Fatalf("constant overflow")
}
return 0
lines += p.file.Lines
p.file = nil // release memory
- if nsyntaxerrors != 0 {
+ if SyntaxErrors() != 0 {
errorexit()
}
// Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
val := p.basicLit(imp.Path)
ipkg := importfile(&val)
if ipkg == nil {
- if nerrors == 0 {
+ if Errors() == 0 {
Fatalf("phase error in import")
}
return
}
if fn.Type == nil {
- if nerrors == 0 {
+ if Errors() == 0 {
Fatalf("funccompile missing type")
}
return
}
func compile(fn *Node) {
- saveerrors()
-
+ errorsBefore := Errors()
order(fn)
- if nerrors != 0 {
+ if Errors() > errorsBefore {
return
}
fn.Func.initLSym(true)
walk(fn)
- if nerrors != 0 {
+ if Errors() > errorsBefore {
return
}
if instrumenting {
--- /dev/null
+// Copyright 2020 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 gc
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "fmt"
+ "os"
+ "runtime/debug"
+ "sort"
+ "strings"
+)
+
+// An errorMsg is a queued error message, waiting to be printed.
+type errorMsg struct {
+ pos src.XPos
+ msg string
+}
+
+// Pos is the current source position being processed,
+// printed by Errorf, ErrorfLang, Fatalf, and Warnf.
+var lineno src.XPos
+
+var (
+ errorMsgs []errorMsg
+ numErrors int // number of entries in errorMsgs that are errors (as opposed to warnings)
+ numSyntaxErrors int
+)
+
+// Errors returns the number of errors reported.
+func Errors() int {
+ return numErrors
+}
+
+// SyntaxErrors returns the number of syntax errors reported
+func SyntaxErrors() int {
+ return numSyntaxErrors
+}
+
+// addErrorMsg adds a new errorMsg (which may be a warning) to errorMsgs.
+func addErrorMsg(pos src.XPos, format string, args ...interface{}) {
+ msg := fmt.Sprintf(format, args...)
+ // Only add the position if know the position.
+ // See issue golang.org/issue/11361.
+ if pos.IsKnown() {
+ msg = fmt.Sprintf("%v: %s", linestr(pos), msg)
+ }
+ errorMsgs = append(errorMsgs, errorMsg{
+ pos: pos,
+ msg: msg + "\n",
+ })
+}
+
+// FmtPos formats pos as a file:line string.
+func linestr(pos src.XPos) string {
+ if Ctxt == nil {
+ return "???"
+ }
+ return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1)
+}
+
+// byPos sorts errors by source position.
+type byPos []errorMsg
+
+func (x byPos) Len() int { return len(x) }
+func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) }
+func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+// FlushErrors sorts errors seen so far by line number, prints them to stdout,
+// and empties the errors array.
+func flusherrors() {
+ Ctxt.Bso.Flush()
+ if len(errorMsgs) == 0 {
+ return
+ }
+ sort.Stable(byPos(errorMsgs))
+ for i, err := range errorMsgs {
+ if i == 0 || err.msg != errorMsgs[i-1].msg {
+ fmt.Printf("%s", err.msg)
+ }
+ }
+ errorMsgs = errorMsgs[:0]
+}
+
+// lasterror keeps track of the most recently issued error,
+// to avoid printing multiple error messages on the same line.
+var lasterror struct {
+ syntax src.XPos // source position of last syntax error
+ other src.XPos // source position of last non-syntax error
+ msg string // error message of last non-syntax error
+}
+
+// sameline reports whether two positions a, b are on the same line.
+func sameline(a, b src.XPos) bool {
+ p := Ctxt.PosTable.Pos(a)
+ q := Ctxt.PosTable.Pos(b)
+ return p.Base() == q.Base() && p.Line() == q.Line()
+}
+
+// Errorf reports a formatted error at the current line.
+func yyerror(format string, args ...interface{}) {
+ yyerrorl(lineno, format, args...)
+}
+
+// ErrorfAt reports a formatted error message at pos.
+func yyerrorl(pos src.XPos, format string, args ...interface{}) {
+ msg := fmt.Sprintf(format, args...)
+
+ if strings.HasPrefix(msg, "syntax error") {
+ numSyntaxErrors++
+ // only one syntax error per line, no matter what error
+ if sameline(lasterror.syntax, pos) {
+ return
+ }
+ lasterror.syntax = pos
+ } else {
+ // only one of multiple equal non-syntax errors per line
+ // (flusherrors shows only one of them, so we filter them
+ // here as best as we can (they may not appear in order)
+ // so that we don't count them here and exit early, and
+ // then have nothing to show for.)
+ if sameline(lasterror.other, pos) && lasterror.msg == msg {
+ return
+ }
+ lasterror.other = pos
+ lasterror.msg = msg
+ }
+
+ addErrorMsg(pos, "%s", msg)
+ numErrors++
+
+ hcrash()
+ if numErrors >= 10 && Debug.e == 0 {
+ flusherrors()
+ fmt.Printf("%v: too many errors\n", linestr(pos))
+ errorexit()
+ }
+}
+
+// ErrorfVers reports that a language feature (format, args) requires a later version of Go.
+func yyerrorv(lang string, format string, args ...interface{}) {
+ yyerror("%s requires %s or later (-lang was set to %s; check go.mod)", fmt.Sprintf(format, args...), lang, flag_lang)
+}
+
+// UpdateErrorDot is a clumsy hack that rewrites the last error,
+// if it was "LINE: undefined: NAME", to be "LINE: undefined: NAME in EXPR".
+// It is used to give better error messages for dot (selector) expressions.
+func UpdateErrorDot(line string, name, expr string) {
+ if len(errorMsgs) == 0 {
+ return
+ }
+ e := &errorMsgs[len(errorMsgs)-1]
+ if strings.HasPrefix(e.msg, line) && e.msg == fmt.Sprintf("%v: undefined: %v\n", line, name) {
+ e.msg = fmt.Sprintf("%v: undefined: %v in %v\n", line, name, expr)
+ }
+}
+
+// Warnf reports a formatted warning at the current line.
+// In general the Go compiler does NOT generate warnings,
+// so this should be used only when the user has opted in
+// to additional output by setting a particular flag.
+func Warn(format string, args ...interface{}) {
+ Warnl(lineno, format, args...)
+}
+
+// WarnfAt reports a formatted warning at pos.
+// In general the Go compiler does NOT generate warnings,
+// so this should be used only when the user has opted in
+// to additional output by setting a particular flag.
+func Warnl(pos src.XPos, format string, args ...interface{}) {
+ addErrorMsg(pos, format, args...)
+ if Debug.m != 0 {
+ flusherrors()
+ }
+}
+
+// Fatal reports a fatal error - an internal problem - at the current line and exits.
+// If other errors have already been printed, then Fatal just quietly exits.
+// (The internal problem may have been caused by incomplete information
+// after the already-reported errors, so best to let users fix those and
+// try again without being bothered about a spurious internal error.)
+//
+// But if no errors have been printed, or if -d panic has been specified,
+// Fatal prints the error as an "internal compiler error". In a released build,
+// it prints an error asking to file a bug report. In development builds, it
+// prints a stack trace.
+//
+// If -h has been specified, Fatal panics to force the usual runtime info dump.
+func Fatalf(format string, args ...interface{}) {
+ flusherrors()
+
+ if Debug_panic != 0 || numErrors == 0 {
+ fmt.Printf("%v: internal compiler error: ", linestr(lineno))
+ fmt.Printf(format, args...)
+ fmt.Printf("\n")
+
+ // If this is a released compiler version, ask for a bug report.
+ if strings.HasPrefix(objabi.Version, "go") {
+ fmt.Printf("\n")
+ fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
+ fmt.Printf("https://golang.org/issue/new\n")
+ } else {
+ // Not a release; dump a stack trace, too.
+ fmt.Println()
+ os.Stdout.Write(debug.Stack())
+ fmt.Println()
+ }
+ }
+
+ hcrash()
+ errorexit()
+}
+
+// hcrash crashes the compiler when -h is set, to find out where a message is generated.
+func hcrash() {
+ if Debug.h != 0 {
+ flusherrors()
+ if outfile != "" {
+ os.Remove(outfile)
+ }
+ panic("-h")
+ }
+}
+
+// ErrorExit handles an error-status exit.
+// It flushes any pending errors, removes the output file, and exits.
+func errorexit() {
+ flusherrors()
+ if outfile != "" {
+ os.Remove(outfile)
+ }
+ os.Exit(2)
+}
+
+// ExitIfErrors calls ErrorExit if any errors have been reported.
+func ExitIfErrors() {
+ if Errors() > 0 {
+ errorexit()
+ }
+}
import (
"cmd/compile/internal/types"
- "cmd/internal/objabi"
"cmd/internal/src"
"crypto/md5"
"encoding/binary"
"fmt"
- "os"
- "runtime/debug"
"sort"
"strconv"
"strings"
"unicode/utf8"
)
-type Error struct {
- pos src.XPos
- msg string
-}
-
-var errors []Error
-
// largeStack is info about a function whose stack frame is too large (rare).
type largeStack struct {
locals int64
largeStackFrames []largeStack
)
-func errorexit() {
- flusherrors()
- if outfile != "" {
- os.Remove(outfile)
- }
- os.Exit(2)
-}
-
-func adderrorname(n *Node) {
- if n.Op != ODOT {
- return
- }
- old := fmt.Sprintf("%v: undefined: %v\n", n.Line(), n.Left)
- if len(errors) > 0 && errors[len(errors)-1].pos.Line() == n.Pos.Line() && errors[len(errors)-1].msg == old {
- errors[len(errors)-1].msg = fmt.Sprintf("%v: undefined: %v in %v\n", n.Line(), n.Left, n)
- }
-}
-
-func adderr(pos src.XPos, format string, args ...interface{}) {
- msg := fmt.Sprintf(format, args...)
- // Only add the position if know the position.
- // See issue golang.org/issue/11361.
- if pos.IsKnown() {
- msg = fmt.Sprintf("%v: %s", linestr(pos), msg)
- }
- errors = append(errors, Error{
- pos: pos,
- msg: msg + "\n",
- })
-}
-
-// byPos sorts errors by source position.
-type byPos []Error
-
-func (x byPos) Len() int { return len(x) }
-func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) }
-func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
-// flusherrors sorts errors seen so far by line number, prints them to stdout,
-// and empties the errors array.
-func flusherrors() {
- Ctxt.Bso.Flush()
- if len(errors) == 0 {
- return
- }
- sort.Stable(byPos(errors))
- for i, err := range errors {
- if i == 0 || err.msg != errors[i-1].msg {
- fmt.Printf("%s", err.msg)
- }
- }
- errors = errors[:0]
-}
-
-func hcrash() {
- if Debug.h != 0 {
- flusherrors()
- if outfile != "" {
- os.Remove(outfile)
- }
- var x *int
- *x = 0
- }
-}
-
-func linestr(pos src.XPos) string {
- return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1)
-}
-
-// lasterror keeps track of the most recently issued error.
-// It is used to avoid multiple error messages on the same
-// line.
-var lasterror struct {
- syntax src.XPos // source position of last syntax error
- other src.XPos // source position of last non-syntax error
- msg string // error message of last non-syntax error
-}
-
-// sameline reports whether two positions a, b are on the same line.
-func sameline(a, b src.XPos) bool {
- p := Ctxt.PosTable.Pos(a)
- q := Ctxt.PosTable.Pos(b)
- return p.Base() == q.Base() && p.Line() == q.Line()
-}
-
-func yyerrorl(pos src.XPos, format string, args ...interface{}) {
- msg := fmt.Sprintf(format, args...)
-
- if strings.HasPrefix(msg, "syntax error") {
- nsyntaxerrors++
- // only one syntax error per line, no matter what error
- if sameline(lasterror.syntax, pos) {
- return
- }
- lasterror.syntax = pos
- } else {
- // only one of multiple equal non-syntax errors per line
- // (flusherrors shows only one of them, so we filter them
- // here as best as we can (they may not appear in order)
- // so that we don't count them here and exit early, and
- // then have nothing to show for.)
- if sameline(lasterror.other, pos) && lasterror.msg == msg {
- return
- }
- lasterror.other = pos
- lasterror.msg = msg
- }
-
- adderr(pos, "%s", msg)
-
- hcrash()
- nerrors++
- if nsavederrors+nerrors >= 10 && Debug.e == 0 {
- flusherrors()
- fmt.Printf("%v: too many errors\n", linestr(pos))
- errorexit()
- }
-}
-
-func yyerrorv(lang string, format string, args ...interface{}) {
- what := fmt.Sprintf(format, args...)
- yyerrorl(lineno, "%s requires %s or later (-lang was set to %s; check go.mod)", what, lang, flag_lang)
-}
-
-func yyerror(format string, args ...interface{}) {
- yyerrorl(lineno, format, args...)
-}
-
-func Warn(fmt_ string, args ...interface{}) {
- Warnl(lineno, fmt_, args...)
-}
-
-func Warnl(line src.XPos, fmt_ string, args ...interface{}) {
- adderr(line, fmt_, args...)
- if Debug.m != 0 {
- flusherrors()
- }
-}
-
-func Fatalf(fmt_ string, args ...interface{}) {
- flusherrors()
-
- if Debug_panic != 0 || nsavederrors+nerrors == 0 {
- fmt.Printf("%v: internal compiler error: ", linestr(lineno))
- fmt.Printf(fmt_, args...)
- fmt.Printf("\n")
-
- // If this is a released compiler version, ask for a bug report.
- if strings.HasPrefix(objabi.Version, "go") {
- fmt.Printf("\n")
- fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
- fmt.Printf("https://golang.org/issue/new\n")
- } else {
- // Not a release; dump a stack trace, too.
- fmt.Println()
- os.Stdout.Write(debug.Stack())
- fmt.Println()
- }
- }
-
- hcrash()
- errorexit()
-}
-
// hasUniquePos reports whether n has a unique position that can be
// used for reporting error messages.
//
yyerrorl(n.Pos, "constant definition loop%s", cycleTrace(cycleFor(n)))
}
- if nsavederrors+nerrors == 0 {
+ if Errors() == 0 {
var trace string
for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
x := typecheck_tcstack[i]
t := n.Left.Type
if t == nil {
- adderrorname(n)
+ UpdateErrorDot(n.Line(), n.Left.String(), n.String())
n.Type = nil
return n
}
if n.SubOp() != 0 { // like OPRINTN
break
}
- if nsavederrors+nerrors > 0 {
+ if Errors() > 0 {
// Can have undefined variables in x := foo
// that make x have an n.name.Defn == nil.
// If there are other errors anyway, don't
n.SetWalkdef(1)
setTypeNode(n, types.New(TFORW))
n.Type.Sym = n.Sym
- nerrors0 := nerrors
+ errorsBefore := Errors()
typecheckdeftype(n)
- if n.Type.Etype == TFORW && nerrors > nerrors0 {
+ if n.Type.Etype == TFORW && Errors() > errorsBefore {
// Something went wrong during type-checking,
// but it was reported. Silence future errors.
n.Type.SetBroke(true)
func walk(fn *Node) {
Curfn = fn
+ errorsBefore := Errors()
if Debug.W != 0 {
s := fmt.Sprintf("\nbefore walk %v", Curfn.Func.Nname.Sym)
}
lineno = lno
- if nerrors != 0 {
+ if Errors() > errorsBefore {
return
}
walkstmtlist(Curfn.Nbody.Slice())