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