github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/labels.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package types2 6 7 import ( 8 "github.com/go-asm/go/cmd/compile/syntax" 9 . "github.com/go-asm/go/types/errors" 10 ) 11 12 // labels checks correct label use in body. 13 func (check *Checker) labels(body *syntax.BlockStmt) { 14 // set of all labels in this body 15 all := NewScope(nil, body.Pos(), syntax.EndPos(body), "label") 16 17 fwdJumps := check.blockBranches(all, nil, nil, body.List) 18 19 // If there are any forward jumps left, no label was found for 20 // the corresponding goto statements. Either those labels were 21 // never defined, or they are inside blocks and not reachable 22 // for the respective gotos. 23 for _, jmp := range fwdJumps { 24 var msg string 25 var code Code 26 name := jmp.Label.Value 27 if alt := all.Lookup(name); alt != nil { 28 msg = "goto %s jumps into block" 29 alt.(*Label).used = true // avoid another error 30 code = JumpIntoBlock 31 } else { 32 msg = "label %s not declared" 33 code = UndeclaredLabel 34 } 35 check.errorf(jmp.Label, code, msg, name) 36 } 37 38 // spec: "It is illegal to define a label that is never used." 39 for name, obj := range all.elems { 40 obj = resolve(name, obj) 41 if lbl := obj.(*Label); !lbl.used { 42 check.softErrorf(lbl.pos, UnusedLabel, "label %s declared and not used", lbl.name) 43 } 44 } 45 } 46 47 // A block tracks label declarations in a block and its enclosing blocks. 48 type block struct { 49 parent *block // enclosing block 50 lstmt *syntax.LabeledStmt // labeled statement to which this block belongs, or nil 51 labels map[string]*syntax.LabeledStmt // allocated lazily 52 } 53 54 // insert records a new label declaration for the current block. 55 // The label must not have been declared before in any block. 56 func (b *block) insert(s *syntax.LabeledStmt) { 57 name := s.Label.Value 58 if debug { 59 assert(b.gotoTarget(name) == nil) 60 } 61 labels := b.labels 62 if labels == nil { 63 labels = make(map[string]*syntax.LabeledStmt) 64 b.labels = labels 65 } 66 labels[name] = s 67 } 68 69 // gotoTarget returns the labeled statement in the current 70 // or an enclosing block with the given label name, or nil. 71 func (b *block) gotoTarget(name string) *syntax.LabeledStmt { 72 for s := b; s != nil; s = s.parent { 73 if t := s.labels[name]; t != nil { 74 return t 75 } 76 } 77 return nil 78 } 79 80 // enclosingTarget returns the innermost enclosing labeled 81 // statement with the given label name, or nil. 82 func (b *block) enclosingTarget(name string) *syntax.LabeledStmt { 83 for s := b; s != nil; s = s.parent { 84 if t := s.lstmt; t != nil && t.Label.Value == name { 85 return t 86 } 87 } 88 return nil 89 } 90 91 // blockBranches processes a block's statement list and returns the set of outgoing forward jumps. 92 // all is the scope of all declared labels, parent the set of labels declared in the immediately 93 // enclosing block, and lstmt is the labeled statement this block is associated with (or nil). 94 func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.LabeledStmt, list []syntax.Stmt) []*syntax.BranchStmt { 95 b := &block{parent, lstmt, nil} 96 97 var ( 98 varDeclPos syntax.Pos 99 fwdJumps, badJumps []*syntax.BranchStmt 100 ) 101 102 // All forward jumps jumping over a variable declaration are possibly 103 // invalid (they may still jump out of the block and be ok). 104 // recordVarDecl records them for the given position. 105 recordVarDecl := func(pos syntax.Pos) { 106 varDeclPos = pos 107 badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps 108 } 109 110 jumpsOverVarDecl := func(jmp *syntax.BranchStmt) bool { 111 if varDeclPos.IsKnown() { 112 for _, bad := range badJumps { 113 if jmp == bad { 114 return true 115 } 116 } 117 } 118 return false 119 } 120 121 var stmtBranches func(syntax.Stmt) 122 stmtBranches = func(s syntax.Stmt) { 123 switch s := s.(type) { 124 case *syntax.DeclStmt: 125 for _, d := range s.DeclList { 126 if d, _ := d.(*syntax.VarDecl); d != nil { 127 recordVarDecl(d.Pos()) 128 } 129 } 130 131 case *syntax.LabeledStmt: 132 // declare non-blank label 133 if name := s.Label.Value; name != "_" { 134 lbl := NewLabel(s.Label.Pos(), check.pkg, name) 135 if alt := all.Insert(lbl); alt != nil { 136 var err error_ 137 err.code = DuplicateLabel 138 err.soft = true 139 err.errorf(lbl.pos, "label %s already declared", name) 140 err.recordAltDecl(alt) 141 check.report(&err) 142 // ok to continue 143 } else { 144 b.insert(s) 145 check.recordDef(s.Label, lbl) 146 } 147 // resolve matching forward jumps and remove them from fwdJumps 148 i := 0 149 for _, jmp := range fwdJumps { 150 if jmp.Label.Value == name { 151 // match 152 lbl.used = true 153 check.recordUse(jmp.Label, lbl) 154 if jumpsOverVarDecl(jmp) { 155 check.softErrorf( 156 jmp.Label, 157 JumpOverDecl, 158 "goto %s jumps over variable declaration at line %d", 159 name, 160 varDeclPos.Line(), 161 ) 162 // ok to continue 163 } 164 } else { 165 // no match - record new forward jump 166 fwdJumps[i] = jmp 167 i++ 168 } 169 } 170 fwdJumps = fwdJumps[:i] 171 lstmt = s 172 } 173 stmtBranches(s.Stmt) 174 175 case *syntax.BranchStmt: 176 if s.Label == nil { 177 return // checked in 1st pass (check.stmt) 178 } 179 180 // determine and validate target 181 name := s.Label.Value 182 switch s.Tok { 183 case syntax.Break: 184 // spec: "If there is a label, it must be that of an enclosing 185 // "for", "switch", or "select" statement, and that is the one 186 // whose execution terminates." 187 valid := false 188 if t := b.enclosingTarget(name); t != nil { 189 switch t.Stmt.(type) { 190 case *syntax.SwitchStmt, *syntax.SelectStmt, *syntax.ForStmt: 191 valid = true 192 } 193 } 194 if !valid { 195 check.errorf(s.Label, MisplacedLabel, "invalid break label %s", name) 196 return 197 } 198 199 case syntax.Continue: 200 // spec: "If there is a label, it must be that of an enclosing 201 // "for" statement, and that is the one whose execution advances." 202 valid := false 203 if t := b.enclosingTarget(name); t != nil { 204 switch t.Stmt.(type) { 205 case *syntax.ForStmt: 206 valid = true 207 } 208 } 209 if !valid { 210 check.errorf(s.Label, MisplacedLabel, "invalid continue label %s", name) 211 return 212 } 213 214 case syntax.Goto: 215 if b.gotoTarget(name) == nil { 216 // label may be declared later - add branch to forward jumps 217 fwdJumps = append(fwdJumps, s) 218 return 219 } 220 221 default: 222 check.errorf(s, InvalidSyntaxTree, "branch statement: %s %s", s.Tok, name) 223 return 224 } 225 226 // record label use 227 obj := all.Lookup(name) 228 obj.(*Label).used = true 229 check.recordUse(s.Label, obj) 230 231 case *syntax.AssignStmt: 232 if s.Op == syntax.Def { 233 recordVarDecl(s.Pos()) 234 } 235 236 case *syntax.BlockStmt: 237 // Unresolved forward jumps inside the nested block 238 // become forward jumps in the current block. 239 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...) 240 241 case *syntax.IfStmt: 242 stmtBranches(s.Then) 243 if s.Else != nil { 244 stmtBranches(s.Else) 245 } 246 247 case *syntax.SwitchStmt: 248 b := &block{b, lstmt, nil} 249 for _, s := range s.Body { 250 fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...) 251 } 252 253 case *syntax.SelectStmt: 254 b := &block{b, lstmt, nil} 255 for _, s := range s.Body { 256 fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...) 257 } 258 259 case *syntax.ForStmt: 260 stmtBranches(s.Body) 261 } 262 } 263 264 for _, s := range list { 265 stmtBranches(s) 266 } 267 268 return fwdJumps 269 }