1// Copyright 2021 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
5package noder
6
7import (
8	"fmt"
9	"internal/buildcfg"
10	"internal/types/errors"
11	"regexp"
12	"sort"
13
14	"cmd/compile/internal/base"
15	"cmd/compile/internal/rangefunc"
16	"cmd/compile/internal/syntax"
17	"cmd/compile/internal/types2"
18	"cmd/internal/src"
19)
20
21var versionErrorRx = regexp.MustCompile(`requires go[0-9]+\.[0-9]+ or later`)
22
23// checkFiles configures and runs the types2 checker on the given
24// parsed source files and then returns the result.
25// The map result value indicates which closures are generated from the bodies of range function loops.
26func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info, map[*syntax.FuncLit]bool) {
27	if base.SyntaxErrors() != 0 {
28		base.ErrorExit()
29	}
30
31	// setup and syntax error reporting
32	files := make([]*syntax.File, len(noders))
33	// fileBaseMap maps all file pos bases back to *syntax.File
34	// for checking Go version mismatched.
35	fileBaseMap := make(map[*syntax.PosBase]*syntax.File)
36	for i, p := range noders {
37		files[i] = p.file
38		// The file.Pos() is the position of the package clause.
39		// If there's a //line directive before that, file.Pos().Base()
40		// refers to that directive, not the file itself.
41		// Make sure to consistently map back to file base, here and
42		// when we look for a file in the conf.Error handler below,
43		// otherwise the file may not be found (was go.dev/issue/67141).
44		fileBaseMap[p.file.Pos().FileBase()] = p.file
45	}
46
47	// typechecking
48	ctxt := types2.NewContext()
49	importer := gcimports{
50		ctxt:     ctxt,
51		packages: make(map[string]*types2.Package),
52	}
53	conf := types2.Config{
54		Context:            ctxt,
55		GoVersion:          base.Flag.Lang,
56		IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode
57		Importer:           &importer,
58		Sizes:              types2.SizesFor("gc", buildcfg.GOARCH),
59		EnableAlias:        true,
60	}
61	if base.Flag.ErrorURL {
62		conf.ErrorURL = " [go.dev/e/%s]"
63	}
64	info := &types2.Info{
65		StoreTypesInSyntax: true,
66		Defs:               make(map[*syntax.Name]types2.Object),
67		Uses:               make(map[*syntax.Name]types2.Object),
68		Selections:         make(map[*syntax.SelectorExpr]*types2.Selection),
69		Implicits:          make(map[syntax.Node]types2.Object),
70		Scopes:             make(map[syntax.Node]*types2.Scope),
71		Instances:          make(map[*syntax.Name]types2.Instance),
72		FileVersions:       make(map[*syntax.PosBase]string),
73		// expand as needed
74	}
75	conf.Error = func(err error) {
76		terr := err.(types2.Error)
77		msg := terr.Msg
78		if versionErrorRx.MatchString(msg) {
79			fileBase := terr.Pos.FileBase()
80			fileVersion := info.FileVersions[fileBase]
81			file := fileBaseMap[fileBase]
82			if file == nil {
83				// This should never happen, but be careful and don't crash.
84			} else if file.GoVersion == fileVersion {
85				// If we have a version error caused by //go:build, report it.
86				msg = fmt.Sprintf("%s (file declares //go:build %s)", msg, fileVersion)
87			} else {
88				// Otherwise, hint at the -lang setting.
89				msg = fmt.Sprintf("%s (-lang was set to %s; check go.mod)", msg, base.Flag.Lang)
90			}
91		}
92		base.ErrorfAt(m.makeXPos(terr.Pos), terr.Code, "%s", msg)
93	}
94
95	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
96	base.ExitIfErrors()
97	if err != nil {
98		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
99	}
100
101	// Check for anonymous interface cycles (#56103).
102	// TODO(gri) move this code into the type checkers (types2 and go/types)
103	var f cycleFinder
104	for _, file := range files {
105		syntax.Inspect(file, func(n syntax.Node) bool {
106			if n, ok := n.(*syntax.InterfaceType); ok {
107				if f.hasCycle(types2.Unalias(n.GetTypeInfo().Type).(*types2.Interface)) {
108					base.ErrorfAt(m.makeXPos(n.Pos()), errors.InvalidTypeCycle, "invalid recursive type: anonymous interface refers to itself (see https://go.dev/issue/56103)")
109
110					for typ := range f.cyclic {
111						f.cyclic[typ] = false // suppress duplicate errors
112					}
113				}
114				return false
115			}
116			return true
117		})
118	}
119	base.ExitIfErrors()
120
121	// Implementation restriction: we don't allow not-in-heap types to
122	// be used as type arguments (#54765).
123	{
124		type nihTarg struct {
125			pos src.XPos
126			typ types2.Type
127		}
128		var nihTargs []nihTarg
129
130		for name, inst := range info.Instances {
131			for i := 0; i < inst.TypeArgs.Len(); i++ {
132				if targ := inst.TypeArgs.At(i); isNotInHeap(targ) {
133					nihTargs = append(nihTargs, nihTarg{m.makeXPos(name.Pos()), targ})
134				}
135			}
136		}
137		sort.Slice(nihTargs, func(i, j int) bool {
138			ti, tj := nihTargs[i], nihTargs[j]
139			return ti.pos.Before(tj.pos)
140		})
141		for _, targ := range nihTargs {
142			base.ErrorfAt(targ.pos, 0, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ)
143		}
144	}
145	base.ExitIfErrors()
146
147	// Rewrite range over function to explicit function calls
148	// with the loop bodies converted into new implicit closures.
149	// We do this now, before serialization to unified IR, so that if the
150	// implicit closures are inlined, we will have the unified IR form.
151	// If we do the rewrite in the back end, like between typecheck and walk,
152	// then the new implicit closure will not have a unified IR inline body,
153	// and bodyReaderFor will fail.
154	rangeInfo := rangefunc.Rewrite(pkg, info, files)
155
156	return pkg, info, rangeInfo
157}
158
159// A cycleFinder detects anonymous interface cycles (go.dev/issue/56103).
160type cycleFinder struct {
161	cyclic map[*types2.Interface]bool
162}
163
164// hasCycle reports whether typ is part of an anonymous interface cycle.
165func (f *cycleFinder) hasCycle(typ *types2.Interface) bool {
166	// We use Method instead of ExplicitMethod to implicitly expand any
167	// embedded interfaces. Then we just need to walk any anonymous
168	// types, keeping track of *types2.Interface types we visit along
169	// the way.
170	for i := 0; i < typ.NumMethods(); i++ {
171		if f.visit(typ.Method(i).Type()) {
172			return true
173		}
174	}
175	return false
176}
177
178// visit recursively walks typ0 to check any referenced interface types.
179func (f *cycleFinder) visit(typ0 types2.Type) bool {
180	for { // loop for tail recursion
181		switch typ := types2.Unalias(typ0).(type) {
182		default:
183			base.Fatalf("unexpected type: %T", typ)
184
185		case *types2.Basic, *types2.Named, *types2.TypeParam:
186			return false // named types cannot be part of an anonymous cycle
187		case *types2.Pointer:
188			typ0 = typ.Elem()
189		case *types2.Array:
190			typ0 = typ.Elem()
191		case *types2.Chan:
192			typ0 = typ.Elem()
193		case *types2.Map:
194			if f.visit(typ.Key()) {
195				return true
196			}
197			typ0 = typ.Elem()
198		case *types2.Slice:
199			typ0 = typ.Elem()
200
201		case *types2.Struct:
202			for i := 0; i < typ.NumFields(); i++ {
203				if f.visit(typ.Field(i).Type()) {
204					return true
205				}
206			}
207			return false
208
209		case *types2.Interface:
210			// The empty interface (e.g., "any") cannot be part of a cycle.
211			if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 0 {
212				return false
213			}
214
215			// As an optimization, we wait to allocate cyclic here, after
216			// we've found at least one other (non-empty) anonymous
217			// interface. This means when a cycle is present, we need to
218			// make an extra recursive call to actually detect it. But for
219			// most packages, it allows skipping the map allocation
220			// entirely.
221			if x, ok := f.cyclic[typ]; ok {
222				return x
223			}
224			if f.cyclic == nil {
225				f.cyclic = make(map[*types2.Interface]bool)
226			}
227			f.cyclic[typ] = true
228			if f.hasCycle(typ) {
229				return true
230			}
231			f.cyclic[typ] = false
232			return false
233
234		case *types2.Signature:
235			return f.visit(typ.Params()) || f.visit(typ.Results())
236		case *types2.Tuple:
237			for i := 0; i < typ.Len(); i++ {
238				if f.visit(typ.At(i).Type()) {
239					return true
240				}
241			}
242			return false
243		}
244	}
245}
246