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