From 703bd8362350a0c1742d6eb9017ebbf13fcbbbdd Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Thu, 3 Sep 2015 17:07:00 +1000 Subject: [PATCH] cmd/compile: use []*Node instead of NodeList in sinit This is a first of a set of changes to make the transition away from NodeList easier by removing cases in which NodeList doesn't act semi-trivially like a []*Node. This CL was originally prepared by Josh Bleecher Snyder . This change passes go build -toolexec 'toolstash -cmp' -a std. Change-Id: Iad10b75e42b5b24e1694407841282fa3bab2dc9f Reviewed-on: https://go-review.googlesource.com/14232 TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/sinit.go | 125 ++++++++++++--------------- test/initloop.go | 17 ++++ 2 files changed, 71 insertions(+), 71 deletions(-) create mode 100644 test/initloop.go diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index 27bcb0bdac..74e8b55bf1 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -19,7 +19,7 @@ const ( ) var ( - initlist *NodeList + initlist []*Node initplans map[*Node]*InitPlan inittemps = make(map[*Node]*Node) ) @@ -47,15 +47,12 @@ func init1(n *Node, out **NodeList) { } switch n.Class { case PEXTERN, PFUNC: - break - default: if isblank(n) && n.Name.Curfn == nil && n.Name.Defn != nil && n.Name.Defn.Initorder == InitNotStarted { // blank names initialization is part of init() but not // when they are inside a function. break } - return } @@ -72,90 +69,43 @@ func init1(n *Node, out **NodeList) { // Conversely, if there exists an initialization cycle involving // a variable in the program, the tree walk will reach a cycle // involving that variable. - var nv *Node if n.Class != PFUNC { - nv = n - goto foundinitloop + foundinitloop(n, n) } - for l := initlist; l.N != n; l = l.Next { - if l.N.Class != PFUNC { - nv = l.N - goto foundinitloop + for i := len(initlist) - 1; i >= 0; i-- { + x := initlist[i] + if x == n { + break + } + if x.Class != PFUNC { + foundinitloop(n, x) } } // The loop involves only functions, ok. return - - // if there have already been errors printed, - // those errors probably confused us and - // there might not be a loop. let the user - // fix those first. - foundinitloop: - Flusherrors() - - if nerrors > 0 { - errorexit() - } - - // There is a loop involving nv. We know about - // n and initlist = n1 <- ... <- nv <- ... <- n <- ... - fmt.Printf("%v: initialization loop:\n", nv.Line()) - - // Build back pointers in initlist. - for l := initlist; l != nil; l = l.Next { - if l.Next != nil { - l.Next.End = l - } - } - - // Print nv -> ... -> n1 -> n. - var l *NodeList - for l = initlist; l.N != nv; l = l.Next { - } - for ; l != nil; l = l.End { - fmt.Printf("\t%v %v refers to\n", l.N.Line(), l.N.Sym) - } - - // Print n -> ... -> nv. - for l = initlist; l.N != n; l = l.Next { - } - for ; l.N != nv; l = l.End { - fmt.Printf("\t%v %v refers to\n", l.N.Line(), l.N.Sym) - } - fmt.Printf("\t%v %v\n", nv.Line(), nv.Sym) - errorexit() } // reached a new unvisited node. n.Initorder = InitPending - - l := new(NodeList) - if l == nil { - Flusherrors() - Yyerror("out of memory") - errorexit() - } - - l.Next = initlist - l.N = n - l.End = nil - initlist = l + initlist = append(initlist, n) // make sure that everything n depends on is initialized. // n->defn is an assignment to n if defn := n.Name.Defn; defn != nil { switch defn.Op { default: - goto bad + Dump("defn", defn) + Fatalf("init1: bad defn") case ODCLFUNC: init2list(defn.Nbody, out) case OAS: if defn.Left != n { - goto bad + Dump("defn", defn) + Fatalf("init1: bad defn") } if isblank(defn.Left) && candiscard(defn.Right) { defn.Op = OEMPTY @@ -190,18 +140,51 @@ func init1(n *Node, out **NodeList) { } } - l = initlist - initlist = l.Next - if l.N != n { - Fatalf("bad initlist") + last := len(initlist) - 1 + if initlist[last] != n { + Fatalf("bad initlist %v", initlist) } + initlist[last] = nil // allow GC + initlist = initlist[:last] n.Initorder = InitDone return +} + +// foundinitloop prints an init loop error and exits. +func foundinitloop(node, visited *Node) { + // If there have already been errors printed, + // those errors probably confused us and + // there might not be a loop. Let the user + // fix those first. + Flusherrors() + if nerrors > 0 { + errorexit() + } + + // Find the index of node and visited in the initlist. + var nodeindex, visitedindex int + for ; initlist[nodeindex] != node; nodeindex++ { + } + for ; initlist[visitedindex] != visited; visitedindex++ { + } + + // There is a loop involving visited. We know about node and + // initlist = n1 <- ... <- visited <- ... <- node <- ... + fmt.Printf("%v: initialization loop:\n", visited.Line()) + + // Print visited -> ... -> n1 -> node. + for _, n := range initlist[visitedindex:] { + fmt.Printf("\t%v %v refers to\n", n.Line(), n.Sym) + } + + // Print node -> ... -> visited. + for _, n := range initlist[nodeindex:visitedindex] { + fmt.Printf("\t%v %v refers to\n", n.Line(), n.Sym) + } -bad: - Dump("defn", n.Name.Defn) - Fatalf("init1: bad defn") + fmt.Printf("\t%v %v\n", visited.Line(), visited.Sym) + errorexit() } // recurse over n, doing init1 everywhere. diff --git a/test/initloop.go b/test/initloop.go new file mode 100644 index 0000000000..d90395d753 --- /dev/null +++ b/test/initloop.go @@ -0,0 +1,17 @@ +// errorcheck + +// Copyright 2015 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. + +// Verify that initialization loops are caught +// and that the errors print correctly. + +package main + +var ( + x int = a + a int = b // ERROR "a refers to\n.*b refers to\n.*c refers to\n.*a" + b int = c + c int = a +) -- 2.48.1