Source file
src/go/types/check.go
1
2
3
4
5
6
7 package types
8
9 import (
10 "fmt"
11 "go/ast"
12 "go/constant"
13 "go/token"
14 "internal/godebug"
15 . "internal/types/errors"
16 "strings"
17 "sync/atomic"
18 )
19
20
21 var nopos token.Pos
22 var noposn = atPos(nopos)
23
24
25 const debug = false
26
27
28
29
30
31 var gotypesalias = godebug.New("gotypesalias")
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 var _aliasAny int32
52
53 func aliasAny() bool {
54 v := gotypesalias.Value()
55 useAlias := v != "0"
56 inuse := atomic.LoadInt32(&_aliasAny)
57 if inuse != 0 && useAlias != (inuse > 0) {
58 panic(fmt.Sprintf("gotypealias mutated during type checking, gotypesalias=%s, inuse=%d", v, inuse))
59 }
60 return useAlias
61 }
62
63
64 type exprInfo struct {
65 isLhs bool
66 mode operandMode
67 typ *Basic
68 val constant.Value
69 }
70
71
72
73 type environment struct {
74 decl *declInfo
75 scope *Scope
76 pos token.Pos
77 iota constant.Value
78 errpos positioner
79 inTParamList bool
80 sig *Signature
81 isPanic map[*ast.CallExpr]bool
82 hasLabel bool
83 hasCallOrRecv bool
84 }
85
86
87 func (env *environment) lookup(name string) Object {
88 _, obj := env.scope.LookupParent(name, env.pos)
89 return obj
90 }
91
92
93
94
95
96
97
98 type importKey struct {
99 path, dir string
100 }
101
102
103 type dotImportKey struct {
104 scope *Scope
105 name string
106 }
107
108
109 type action struct {
110 f func()
111 desc *actionDesc
112 }
113
114
115
116 func (a *action) describef(pos positioner, format string, args ...any) {
117 if debug {
118 a.desc = &actionDesc{pos, format, args}
119 }
120 }
121
122
123
124 type actionDesc struct {
125 pos positioner
126 format string
127 args []any
128 }
129
130
131
132 type Checker struct {
133
134
135 conf *Config
136 ctxt *Context
137 fset *token.FileSet
138 pkg *Package
139 *Info
140 version goVersion
141 nextID uint64
142 objMap map[Object]*declInfo
143 impMap map[importKey]*Package
144
145
146
147
148
149
150
151
152
153
154 pkgPathMap map[string]map[string]bool
155 seenPkgMap map[*Package]bool
156
157
158
159
160 files []*ast.File
161 versions map[*ast.File]string
162 imports []*PkgName
163 dotImportMap map[dotImportKey]*PkgName
164 recvTParamMap map[*ast.Ident]*TypeParam
165 brokenAliases map[*TypeName]bool
166 unionTypeSets map[*Union]*_TypeSet
167 mono monoGraph
168
169 firstErr error
170 methods map[*TypeName][]*Func
171 untyped map[ast.Expr]exprInfo
172 delayed []action
173 objPath []Object
174 cleaners []cleaner
175
176
177
178 environment
179
180
181 indent int
182 }
183
184
185 func (check *Checker) addDeclDep(to Object) {
186 from := check.decl
187 if from == nil {
188 return
189 }
190 if _, found := check.objMap[to]; !found {
191 return
192 }
193 from.addDep(to)
194 }
195
196
197
198
199
200
201
202 func (check *Checker) brokenAlias(alias *TypeName) {
203 assert(!check.conf._EnableAlias)
204 if check.brokenAliases == nil {
205 check.brokenAliases = make(map[*TypeName]bool)
206 }
207 check.brokenAliases[alias] = true
208 alias.typ = Typ[Invalid]
209 }
210
211
212 func (check *Checker) validAlias(alias *TypeName, typ Type) {
213 assert(!check.conf._EnableAlias)
214 delete(check.brokenAliases, alias)
215 alias.typ = typ
216 }
217
218
219 func (check *Checker) isBrokenAlias(alias *TypeName) bool {
220 assert(!check.conf._EnableAlias)
221 return check.brokenAliases[alias]
222 }
223
224 func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
225 m := check.untyped
226 if m == nil {
227 m = make(map[ast.Expr]exprInfo)
228 check.untyped = m
229 }
230 m[e] = exprInfo{lhs, mode, typ, val}
231 }
232
233
234
235
236
237
238
239 func (check *Checker) later(f func()) *action {
240 i := len(check.delayed)
241 check.delayed = append(check.delayed, action{f: f})
242 return &check.delayed[i]
243 }
244
245
246 func (check *Checker) push(obj Object) int {
247 check.objPath = append(check.objPath, obj)
248 return len(check.objPath) - 1
249 }
250
251
252 func (check *Checker) pop() Object {
253 i := len(check.objPath) - 1
254 obj := check.objPath[i]
255 check.objPath[i] = nil
256 check.objPath = check.objPath[:i]
257 return obj
258 }
259
260 type cleaner interface {
261 cleanup()
262 }
263
264
265
266 func (check *Checker) needsCleanup(c cleaner) {
267 check.cleaners = append(check.cleaners, c)
268 }
269
270
271
272 func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
273
274 if conf == nil {
275 conf = new(Config)
276 }
277
278
279 if info == nil {
280 info = new(Info)
281 }
282
283
284
285
286
287
288
289
290 conf._EnableAlias = gotypesalias.Value() != "0"
291
292 return &Checker{
293 conf: conf,
294 ctxt: conf.Context,
295 fset: fset,
296 pkg: pkg,
297 Info: info,
298 version: asGoVersion(conf.GoVersion),
299 objMap: make(map[Object]*declInfo),
300 impMap: make(map[importKey]*Package),
301 }
302 }
303
304
305
306 func (check *Checker) initFiles(files []*ast.File) {
307
308 check.files = nil
309 check.imports = nil
310 check.dotImportMap = nil
311
312 check.firstErr = nil
313 check.methods = nil
314 check.untyped = nil
315 check.delayed = nil
316 check.objPath = nil
317 check.cleaners = nil
318
319
320 pkg := check.pkg
321 for _, file := range files {
322 switch name := file.Name.Name; pkg.name {
323 case "":
324 if name != "_" {
325 pkg.name = name
326 } else {
327 check.error(file.Name, BlankPkgName, "invalid package name _")
328 }
329 fallthrough
330
331 case name:
332 check.files = append(check.files, file)
333
334 default:
335 check.errorf(atPos(file.Package), MismatchedPkgName, "package %s; expected package %s", name, pkg.name)
336
337 }
338 }
339
340
341 versions := check.Info.FileVersions
342 if versions == nil {
343 versions = make(map[*ast.File]string)
344 }
345 check.versions = versions
346
347 pkgVersionOk := check.version.isValid()
348 if pkgVersionOk && len(files) > 0 && check.version.cmp(go_current) > 0 {
349 check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
350 check.version, go_current)
351 }
352
353
354 for _, file := range check.files {
355
356
357
358 v := check.conf.GoVersion
359
360
361 if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
362
363
364
365
366
367
368
369
370
371
372 v = string(versionMax(fileVersion, go1_21))
373
374
375
376
377 if fileVersion.cmp(go_current) > 0 {
378
379
380 check.errorf(file.Name, TooNew, "file requires newer Go version %v (application built with %v)", fileVersion, go_current)
381 }
382 }
383 versions[file] = v
384 }
385 }
386
387 func versionMax(a, b goVersion) goVersion {
388 if a.cmp(b) < 0 {
389 return b
390 }
391 return a
392 }
393
394
395 type bailout struct{}
396
397 func (check *Checker) handleBailout(err *error) {
398 switch p := recover().(type) {
399 case nil, bailout:
400
401 *err = check.firstErr
402 default:
403
404 panic(p)
405 }
406 }
407
408
409 func (check *Checker) Files(files []*ast.File) (err error) {
410 if check.pkg == Unsafe {
411
412
413
414 return nil
415 }
416
417
418
419
420
421 defer check.handleBailout(&err)
422 check.checkFiles(files)
423 return
424 }
425
426
427
428
429
430 func (check *Checker) checkFiles(files []*ast.File) {
431
432
433 if check.conf._EnableAlias {
434 if atomic.AddInt32(&_aliasAny, 1) <= 0 {
435 panic("EnableAlias set while !EnableAlias type checking is ongoing")
436 }
437 defer atomic.AddInt32(&_aliasAny, -1)
438 } else {
439 if atomic.AddInt32(&_aliasAny, -1) >= 0 {
440 panic("!EnableAlias set while EnableAlias type checking is ongoing")
441 }
442 defer atomic.AddInt32(&_aliasAny, 1)
443 }
444
445 print := func(msg string) {
446 if check.conf._Trace {
447 fmt.Println()
448 fmt.Println(msg)
449 }
450 }
451
452 print("== initFiles ==")
453 check.initFiles(files)
454
455 print("== collectObjects ==")
456 check.collectObjects()
457
458 print("== packageObjects ==")
459 check.packageObjects()
460
461 print("== processDelayed ==")
462 check.processDelayed(0)
463
464 print("== cleanup ==")
465 check.cleanup()
466
467 print("== initOrder ==")
468 check.initOrder()
469
470 if !check.conf.DisableUnusedImportCheck {
471 print("== unusedImports ==")
472 check.unusedImports()
473 }
474
475 print("== recordUntyped ==")
476 check.recordUntyped()
477
478 if check.firstErr == nil {
479
480 check.monomorph()
481 }
482
483 check.pkg.goVersion = check.conf.GoVersion
484 check.pkg.complete = true
485
486
487 check.imports = nil
488 check.dotImportMap = nil
489 check.pkgPathMap = nil
490 check.seenPkgMap = nil
491 check.recvTParamMap = nil
492 check.brokenAliases = nil
493 check.unionTypeSets = nil
494 check.ctxt = nil
495
496
497 }
498
499
500 func (check *Checker) processDelayed(top int) {
501
502
503
504
505
506
507 for i := top; i < len(check.delayed); i++ {
508 a := &check.delayed[i]
509 if check.conf._Trace {
510 if a.desc != nil {
511 check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...)
512 } else {
513 check.trace(nopos, "-- delayed %p", a.f)
514 }
515 }
516 a.f()
517 if check.conf._Trace {
518 fmt.Println()
519 }
520 }
521 assert(top <= len(check.delayed))
522 check.delayed = check.delayed[:top]
523 }
524
525
526 func (check *Checker) cleanup() {
527
528 for i := 0; i < len(check.cleaners); i++ {
529 check.cleaners[i].cleanup()
530 }
531 check.cleaners = nil
532 }
533
534 func (check *Checker) record(x *operand) {
535
536
537 var typ Type
538 var val constant.Value
539 switch x.mode {
540 case invalid:
541 typ = Typ[Invalid]
542 case novalue:
543 typ = (*Tuple)(nil)
544 case constant_:
545 typ = x.typ
546 val = x.val
547 default:
548 typ = x.typ
549 }
550 assert(x.expr != nil && typ != nil)
551
552 if isUntyped(typ) {
553
554
555 check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
556 } else {
557 check.recordTypeAndValue(x.expr, x.mode, typ, val)
558 }
559 }
560
561 func (check *Checker) recordUntyped() {
562 if !debug && check.Types == nil {
563 return
564 }
565
566 for x, info := range check.untyped {
567 if debug && isTyped(info.typ) {
568 check.dump("%v: %s (type %s) is typed", x.Pos(), x, info.typ)
569 panic("unreachable")
570 }
571 check.recordTypeAndValue(x, info.mode, info.typ, info.val)
572 }
573 }
574
575 func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) {
576 assert(x != nil)
577 assert(typ != nil)
578 if mode == invalid {
579 return
580 }
581 if mode == constant_ {
582 assert(val != nil)
583
584
585 assert(!isValid(typ) || allBasic(typ, IsConstType))
586 }
587 if m := check.Types; m != nil {
588 m[x] = TypeAndValue{mode, typ, val}
589 }
590 }
591
592 func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
593
594
595
596
597 for {
598 check.recordTypeAndValue(f, builtin, sig, nil)
599 switch p := f.(type) {
600 case *ast.Ident, *ast.SelectorExpr:
601 return
602 case *ast.ParenExpr:
603 f = p.X
604 default:
605 panic("unreachable")
606 }
607 }
608 }
609
610
611
612 func (check *Checker) recordCommaOkTypes(x ast.Expr, a []*operand) {
613 assert(x != nil)
614 assert(len(a) == 2)
615 if a[0].mode == invalid {
616 return
617 }
618 t0, t1 := a[0].typ, a[1].typ
619 assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError))
620 if m := check.Types; m != nil {
621 for {
622 tv := m[x]
623 assert(tv.Type != nil)
624 pos := x.Pos()
625 tv.Type = NewTuple(
626 NewVar(pos, check.pkg, "", t0),
627 NewVar(pos, check.pkg, "", t1),
628 )
629 m[x] = tv
630
631 p, _ := x.(*ast.ParenExpr)
632 if p == nil {
633 break
634 }
635 x = p.X
636 }
637 }
638 }
639
640
641
642
643
644
645
646 func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) {
647 ident := instantiatedIdent(expr)
648 assert(ident != nil)
649 assert(typ != nil)
650 if m := check.Instances; m != nil {
651 m[ident] = Instance{newTypeList(targs), typ}
652 }
653 }
654
655 func instantiatedIdent(expr ast.Expr) *ast.Ident {
656 var selOrIdent ast.Expr
657 switch e := expr.(type) {
658 case *ast.IndexExpr:
659 selOrIdent = e.X
660 case *ast.IndexListExpr:
661 selOrIdent = e.X
662 case *ast.SelectorExpr, *ast.Ident:
663 selOrIdent = e
664 }
665 switch x := selOrIdent.(type) {
666 case *ast.Ident:
667 return x
668 case *ast.SelectorExpr:
669 return x.Sel
670 }
671
672
673 var buf strings.Builder
674 buf.WriteString("instantiated ident not found; please report: ")
675 ast.Fprint(&buf, token.NewFileSet(), expr, ast.NotNilFilter)
676 panic(buf.String())
677 }
678
679 func (check *Checker) recordDef(id *ast.Ident, obj Object) {
680 assert(id != nil)
681 if m := check.Defs; m != nil {
682 m[id] = obj
683 }
684 }
685
686 func (check *Checker) recordUse(id *ast.Ident, obj Object) {
687 assert(id != nil)
688 assert(obj != nil)
689 if m := check.Uses; m != nil {
690 m[id] = obj
691 }
692 }
693
694 func (check *Checker) recordImplicit(node ast.Node, obj Object) {
695 assert(node != nil)
696 assert(obj != nil)
697 if m := check.Implicits; m != nil {
698 m[node] = obj
699 }
700 }
701
702 func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
703 assert(obj != nil && (recv == nil || len(index) > 0))
704 check.recordUse(x.Sel, obj)
705 if m := check.Selections; m != nil {
706 m[x] = &Selection{kind, recv, obj, index, indirect}
707 }
708 }
709
710 func (check *Checker) recordScope(node ast.Node, scope *Scope) {
711 assert(node != nil)
712 assert(scope != nil)
713 if m := check.Scopes; m != nil {
714 m[node] = scope
715 }
716 }
717
View as plain text