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