diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/align.go')
| -rw-r--r-- | src/cmd/compile/internal/gc/align.go | 108 |
1 files changed, 94 insertions, 14 deletions
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index ab578ee8c7..a3a0c8fce8 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -5,7 +5,9 @@ package gc import ( + "bytes" "cmd/compile/internal/types" + "fmt" "sort" ) @@ -84,7 +86,7 @@ func expandiface(t *types.Type) { sort.Sort(methcmp(methods)) if int64(len(methods)) >= thearch.MAXWIDTH/int64(Widthptr) { - yyerror("interface too large") + yyerrorl(typePos(t), "interface too large") } for i, m := range methods { m.Offset = int64(i) * int64(Widthptr) @@ -148,7 +150,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 { maxwidth = 1<<31 - 1 } if o >= maxwidth { - yyerror("type %L too large", errtype) + yyerrorl(typePos(errtype), "type %L too large", errtype) o = 8 // small but nonzero } } @@ -173,6 +175,91 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 { return o } +// findTypeLoop searches for an invalid type declaration loop involving +// type t and reports whether one is found. If so, path contains the +// loop. +// +// path points to a slice used for tracking the sequence of types +// visited. Using a pointer to a slice allows the slice capacity to +// grow and limit reallocations. +func findTypeLoop(t *types.Type, path *[]*types.Type) bool { + // We implement a simple DFS loop-finding algorithm. This + // could be faster, but type cycles are rare. + + if t.Sym != nil { + // Declared type. Check for loops and otherwise + // recurse on the type expression used in the type + // declaration. + + for i, x := range *path { + if x == t { + *path = (*path)[i:] + return true + } + } + + *path = append(*path, t) + if p := asNode(t.Nod).Name.Param; p != nil && findTypeLoop(p.Ntype.Type, path) { + return true + } + *path = (*path)[:len(*path)-1] + } else { + // Anonymous type. Recurse on contained types. + + switch t.Etype { + case TARRAY: + if findTypeLoop(t.Elem(), path) { + return true + } + case TSTRUCT: + for _, f := range t.Fields().Slice() { + if findTypeLoop(f.Type, path) { + return true + } + } + case TINTER: + for _, m := range t.Methods().Slice() { + if m.Type.IsInterface() { // embedded interface + if findTypeLoop(m.Type, path) { + return true + } + } + } + } + } + + return false +} + +func reportTypeLoop(t *types.Type) { + if t.Broke() { + return + } + + var l []*types.Type + if !findTypeLoop(t, &l) { + Fatalf("failed to find type loop for: %v", t) + } + + // Rotate loop so that the earliest type declaration is first. + i := 0 + for j, t := range l[1:] { + if typePos(t).Before(typePos(l[i])) { + i = j + 1 + } + } + l = append(l[i:], l[:i]...) + + var msg bytes.Buffer + fmt.Fprintf(&msg, "invalid recursive type %v\n", l[0]) + for _, t := range l { + fmt.Fprintf(&msg, "\t%v: %v refers to\n", linestr(typePos(t)), t) + t.SetBroke(true) + } + fmt.Fprintf(&msg, "\t%v: %v", linestr(typePos(l[0])), l[0]) + yyerrorl(typePos(l[0]), msg.String()) +} + // dowidth calculates and stores the size and alignment for t. // If sizeCalculationDisabled is set, and the size/alignment // have not already been calculated, it calls Fatal. @@ -192,11 +279,7 @@ func dowidth(t *types.Type) { } if t.Width == -2 { - if !t.Broke() { - t.SetBroke(true) - yyerrorl(asNode(t.Nod).Pos, "invalid recursive type %v", t) - } - + reportTypeLoop(t) t.Width = 0 t.Align = 1 return @@ -298,7 +381,7 @@ func dowidth(t *types.Type) { t1 := t.ChanArgs() dowidth(t1) // just in case if t1.Elem().Width >= 1<<16 { - yyerror("channel element type too large (>64kB)") + yyerrorl(typePos(t1), "channel element type too large (>64kB)") } w = 1 // anything will do @@ -308,10 +391,7 @@ func dowidth(t *types.Type) { checkwidth(t.Key()) case TFORW: // should have been filled in - if !t.Broke() { - t.SetBroke(true) - yyerror("invalid recursive type %v", t) - } + reportTypeLoop(t) w = 1 // anything will do case TANY: @@ -334,7 +414,7 @@ func dowidth(t *types.Type) { if t.Elem().Width != 0 { cap := (uint64(thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width) if uint64(t.NumElem()) > cap { - yyerror("type %L larger than address space", t) + yyerrorl(typePos(t), "type %L larger than address space", t) } } w = t.NumElem() * t.Elem().Width @@ -376,7 +456,7 @@ func dowidth(t *types.Type) { } if Widthptr == 4 && w != int64(int32(w)) { - yyerror("type %v too large", t) + yyerrorl(typePos(t), "type %v too large", t) } t.Width = w |
