| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package noder |
| |
| import ( |
| "fmt" |
| "regexp" |
| "sort" |
| |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/dwarfgen" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/syntax" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/compile/internal/types2" |
| "cmd/internal/src" |
| ) |
| |
| var versionErrorRx = regexp.MustCompile(`requires go[0-9]+\.[0-9]+ or later`) |
| |
| // checkFiles configures and runs the types2 checker on the given |
| // parsed source files and then returns the result. |
| func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) { |
| if base.SyntaxErrors() != 0 { |
| base.ErrorExit() |
| } |
| |
| // setup and syntax error reporting |
| var m posMap |
| files := make([]*syntax.File, len(noders)) |
| for i, p := range noders { |
| m.join(&p.posMap) |
| files[i] = p.file |
| } |
| |
| // typechecking |
| ctxt := types2.NewContext() |
| importer := gcimports{ |
| ctxt: ctxt, |
| packages: make(map[string]*types2.Package), |
| } |
| conf := types2.Config{ |
| Context: ctxt, |
| GoVersion: base.Flag.Lang, |
| IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode |
| Error: func(err error) { |
| terr := err.(types2.Error) |
| msg := terr.Msg |
| // if we have a version error, hint at the -lang setting |
| if versionErrorRx.MatchString(msg) { |
| msg = fmt.Sprintf("%s (-lang was set to %s; check go.mod)", msg, base.Flag.Lang) |
| } |
| base.ErrorfAt(m.makeXPos(terr.Pos), "%s", msg) |
| }, |
| Importer: &importer, |
| Sizes: &gcSizes{}, |
| OldComparableSemantics: base.Flag.OldComparable, // default is new comparable semantics |
| } |
| info := &types2.Info{ |
| StoreTypesInSyntax: true, |
| Defs: make(map[*syntax.Name]types2.Object), |
| Uses: make(map[*syntax.Name]types2.Object), |
| Selections: make(map[*syntax.SelectorExpr]*types2.Selection), |
| Implicits: make(map[syntax.Node]types2.Object), |
| Scopes: make(map[syntax.Node]*types2.Scope), |
| Instances: make(map[*syntax.Name]types2.Instance), |
| // expand as needed |
| } |
| |
| pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info) |
| |
| // Check for anonymous interface cycles (#56103). |
| if base.Debug.InterfaceCycles == 0 { |
| var f cycleFinder |
| for _, file := range files { |
| syntax.Inspect(file, func(n syntax.Node) bool { |
| if n, ok := n.(*syntax.InterfaceType); ok { |
| if f.hasCycle(n.GetTypeInfo().Type.(*types2.Interface)) { |
| base.ErrorfAt(m.makeXPos(n.Pos()), "invalid recursive type: anonymous interface refers to itself (see https://go.dev/issue/56103)") |
| |
| for typ := range f.cyclic { |
| f.cyclic[typ] = false // suppress duplicate errors |
| } |
| } |
| return false |
| } |
| return true |
| }) |
| } |
| } |
| |
| // Implementation restriction: we don't allow not-in-heap types to |
| // be used as type arguments (#54765). |
| { |
| type nihTarg struct { |
| pos src.XPos |
| typ types2.Type |
| } |
| var nihTargs []nihTarg |
| |
| for name, inst := range info.Instances { |
| for i := 0; i < inst.TypeArgs.Len(); i++ { |
| if targ := inst.TypeArgs.At(i); isNotInHeap(targ) { |
| nihTargs = append(nihTargs, nihTarg{m.makeXPos(name.Pos()), targ}) |
| } |
| } |
| } |
| sort.Slice(nihTargs, func(i, j int) bool { |
| ti, tj := nihTargs[i], nihTargs[j] |
| return ti.pos.Before(tj.pos) |
| }) |
| for _, targ := range nihTargs { |
| base.ErrorfAt(targ.pos, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ) |
| } |
| } |
| |
| base.ExitIfErrors() |
| if err != nil { |
| base.FatalfAt(src.NoXPos, "conf.Check error: %v", err) |
| } |
| |
| return m, pkg, info |
| } |
| |
| // check2 type checks a Go package using types2, and then generates IR |
| // using the results. |
| func check2(noders []*noder) { |
| m, pkg, info := checkFiles(noders) |
| |
| g := irgen{ |
| target: typecheck.Target, |
| self: pkg, |
| info: info, |
| posMap: m, |
| objs: make(map[types2.Object]*ir.Name), |
| typs: make(map[types2.Type]*types.Type), |
| } |
| g.generate(noders) |
| } |
| |
| // Information about sub-dictionary entries in a dictionary |
| type subDictInfo struct { |
| // Call or XDOT node that requires a dictionary. |
| callNode ir.Node |
| // Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic |
| // method or function call, since this node will get dropped when the generic |
| // method/function call is transformed to a call on the instantiated shape |
| // function. Nil for other kinds of calls or XDOTs. |
| savedXNode ir.Node |
| } |
| |
| // dictInfo is the dictionary format for an instantiation of a generic function with |
| // particular shapes. shapeParams, derivedTypes, subDictCalls, itabConvs, and methodExprClosures |
| // describe the actual dictionary entries in order, and the remaining fields are other info |
| // needed in doing dictionary processing during compilation. |
| type dictInfo struct { |
| // Types substituted for the type parameters, which are shape types. |
| shapeParams []*types.Type |
| // All types derived from those typeparams used in the instantiation. |
| derivedTypes []*types.Type |
| // Nodes in the instantiation that requires a subdictionary. Includes |
| // method and function calls (OCALL), function values (OFUNCINST), method |
| // values/expressions (OXDOT). |
| subDictCalls []subDictInfo |
| // Nodes in the instantiation that are a conversion from a typeparam/derived |
| // type to a specific interface. |
| itabConvs []ir.Node |
| // Method expression closures. For a generic type T with method M(arg1, arg2) res, |
| // these closures are func(rcvr T, arg1, arg2) res. |
| // These closures capture no variables, they are just the generic version of ·f symbols |
| // that live in the dictionary instead of in the readonly globals section. |
| methodExprClosures []methodExprClosure |
| |
| // Mapping from each shape type that substitutes a type param, to its |
| // type bound (which is also substituted with shapes if it is parameterized) |
| shapeToBound map[*types.Type]*types.Type |
| |
| // For type switches on nonempty interfaces, a map from OTYPE entries of |
| // HasShape type, to the interface type we're switching from. |
| type2switchType map[ir.Node]*types.Type |
| |
| startSubDict int // Start of dict entries for subdictionaries |
| startItabConv int // Start of dict entries for itab conversions |
| startMethodExprClosures int // Start of dict entries for closures for method expressions |
| dictLen int // Total number of entries in dictionary |
| } |
| |
| type methodExprClosure struct { |
| idx int // index in list of shape parameters |
| name string // method name |
| } |
| |
| // instInfo is information gathered on an shape instantiation of a function. |
| type instInfo struct { |
| fun *ir.Func // The instantiated function (with body) |
| dictParam *ir.Name // The node inside fun that refers to the dictionary param |
| |
| dictInfo *dictInfo |
| } |
| |
| type irgen struct { |
| target *ir.Package |
| self *types2.Package |
| info *types2.Info |
| |
| posMap |
| objs map[types2.Object]*ir.Name |
| typs map[types2.Type]*types.Type |
| marker dwarfgen.ScopeMarker |
| |
| // laterFuncs records tasks that need to run after all declarations |
| // are processed. |
| laterFuncs []func() |
| // haveEmbed indicates whether the current node belongs to file that |
| // imports "embed" package. |
| haveEmbed bool |
| |
| // exprStmtOK indicates whether it's safe to generate expressions or |
| // statements yet. |
| exprStmtOK bool |
| |
| // types which we need to finish, by doing g.fillinMethods. |
| typesToFinalize []*typeDelayInfo |
| |
| // True when we are compiling a top-level generic function or method. Use to |
| // avoid adding closures of generic functions/methods to the target.Decls |
| // list. |
| topFuncIsGeneric bool |
| |
| // The context during type/function/method declarations that is used to |
| // uniquely name type parameters. We need unique names for type params so we |
| // can be sure they match up correctly between types2-to-types1 translation |
| // and types1 importing. |
| curDecl string |
| } |
| |
| // genInst has the information for creating needed instantiations and modifying |
| // functions to use instantiations. |
| type genInst struct { |
| dnum int // for generating unique dictionary variables |
| |
| // Map from the names of all instantiations to information about the |
| // instantiations. |
| instInfoMap map[*types.Sym]*instInfo |
| |
| // Dictionary syms which we need to finish, by writing out any itabconv |
| // or method expression closure entries. |
| dictSymsToFinalize []*delayInfo |
| |
| // New instantiations created during this round of buildInstantiations(). |
| newInsts []ir.Node |
| } |
| |
| func (g *irgen) later(fn func()) { |
| g.laterFuncs = append(g.laterFuncs, fn) |
| } |
| |
| type delayInfo struct { |
| gf *ir.Name |
| targs []*types.Type |
| sym *types.Sym |
| off int |
| isMeth bool |
| } |
| |
| type typeDelayInfo struct { |
| typ *types2.Named |
| ntyp *types.Type |
| } |
| |
| func (g *irgen) generate(noders []*noder) { |
| types.LocalPkg.Name = g.self.Name() |
| typecheck.TypecheckAllowed = true |
| |
| // Prevent size calculations until we set the underlying type |
| // for all package-block defined types. |
| types.DeferCheckSize() |
| |
| // At this point, types2 has already handled name resolution and |
| // type checking. We just need to map from its object and type |
| // representations to those currently used by the rest of the |
| // compiler. This happens in a few passes. |
| |
| // 1. Process all import declarations. We use the compiler's own |
| // importer for this, rather than types2's gcimporter-derived one, |
| // to handle extensions and inline function bodies correctly. |
| // |
| // Also, we need to do this in a separate pass, because mappings are |
| // instantiated on demand. If we interleaved processing import |
| // declarations with other declarations, it's likely we'd end up |
| // wanting to map an object/type from another source file, but not |
| // yet have the import data it relies on. |
| declLists := make([][]syntax.Decl, len(noders)) |
| Outer: |
| for i, p := range noders { |
| g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma) |
| for j, decl := range p.file.DeclList { |
| switch decl := decl.(type) { |
| case *syntax.ImportDecl: |
| g.importDecl(p, decl) |
| default: |
| declLists[i] = p.file.DeclList[j:] |
| continue Outer // no more ImportDecls |
| } |
| } |
| } |
| |
| // 2. Process all package-block type declarations. As with imports, |
| // we need to make sure all types are properly instantiated before |
| // trying to map any expressions that utilize them. In particular, |
| // we need to make sure type pragmas are already known (see comment |
| // in irgen.typeDecl). |
| // |
| // We could perhaps instead defer processing of package-block |
| // variable initializers and function bodies, like noder does, but |
| // special-casing just package-block type declarations minimizes the |
| // differences between processing package-block and function-scoped |
| // declarations. |
| for _, declList := range declLists { |
| for _, decl := range declList { |
| switch decl := decl.(type) { |
| case *syntax.TypeDecl: |
| g.typeDecl((*ir.Nodes)(&g.target.Decls), decl) |
| } |
| } |
| } |
| types.ResumeCheckSize() |
| |
| // 3. Process all remaining declarations. |
| for i, declList := range declLists { |
| old := g.haveEmbed |
| g.haveEmbed = noders[i].importedEmbed |
| g.decls((*ir.Nodes)(&g.target.Decls), declList) |
| g.haveEmbed = old |
| } |
| g.exprStmtOK = true |
| |
| // 4. Run any "later" tasks. Avoid using 'range' so that tasks can |
| // recursively queue further tasks. (Not currently utilized though.) |
| for len(g.laterFuncs) > 0 { |
| fn := g.laterFuncs[0] |
| g.laterFuncs = g.laterFuncs[1:] |
| fn() |
| } |
| |
| if base.Flag.W > 1 { |
| for _, n := range g.target.Decls { |
| s := fmt.Sprintf("\nafter noder2 %v", n) |
| ir.Dump(s, n) |
| } |
| } |
| |
| for _, p := range noders { |
| // Process linkname and cgo pragmas. |
| p.processPragmas() |
| |
| // Double check for any type-checking inconsistencies. This can be |
| // removed once we're confident in IR generation results. |
| syntax.Crawl(p.file, func(n syntax.Node) bool { |
| g.validate(n) |
| return false |
| }) |
| } |
| |
| if base.Flag.Complete { |
| for _, n := range g.target.Decls { |
| if fn, ok := n.(*ir.Func); ok { |
| if fn.Body == nil && fn.Nname.Sym().Linkname == "" { |
| base.ErrorfAt(fn.Pos(), "missing function body") |
| } |
| } |
| } |
| } |
| |
| // Check for unusual case where noder2 encounters a type error that types2 |
| // doesn't check for (e.g. notinheap incompatibility). |
| base.ExitIfErrors() |
| |
| typecheck.DeclareUniverse() |
| |
| // Create any needed instantiations of generic functions and transform |
| // existing and new functions to use those instantiations. |
| BuildInstantiations() |
| |
| // Remove all generic functions from g.target.Decl, since they have been |
| // used for stenciling, but don't compile. Generic functions will already |
| // have been marked for export as appropriate. |
| j := 0 |
| for i, decl := range g.target.Decls { |
| if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() { |
| g.target.Decls[j] = g.target.Decls[i] |
| j++ |
| } |
| } |
| g.target.Decls = g.target.Decls[:j] |
| |
| base.Assertf(len(g.laterFuncs) == 0, "still have %d later funcs", len(g.laterFuncs)) |
| } |
| |
| func (g *irgen) unhandled(what string, p poser) { |
| base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p) |
| panic("unreachable") |
| } |
| |
| // delayTransform returns true if we should delay all transforms, because we are |
| // creating the nodes for a generic function/method. |
| func (g *irgen) delayTransform() bool { |
| return g.topFuncIsGeneric |
| } |
| |
| func (g *irgen) typeAndValue(x syntax.Expr) syntax.TypeAndValue { |
| tv := x.GetTypeInfo() |
| if tv.Type == nil { |
| base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x) |
| } |
| return tv |
| } |
| |
| func (g *irgen) type2(x syntax.Expr) syntax.Type { |
| tv := x.GetTypeInfo() |
| if tv.Type == nil { |
| base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x) |
| } |
| return tv.Type |
| } |
| |
| // A cycleFinder detects anonymous interface cycles (go.dev/issue/56103). |
| type cycleFinder struct { |
| cyclic map[*types2.Interface]bool |
| } |
| |
| // hasCycle reports whether typ is part of an anonymous interface cycle. |
| func (f *cycleFinder) hasCycle(typ *types2.Interface) bool { |
| // We use Method instead of ExplicitMethod to implicitly expand any |
| // embedded interfaces. Then we just need to walk any anonymous |
| // types, keeping track of *types2.Interface types we visit along |
| // the way. |
| for i := 0; i < typ.NumMethods(); i++ { |
| if f.visit(typ.Method(i).Type()) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // visit recursively walks typ0 to check any referenced interface types. |
| func (f *cycleFinder) visit(typ0 types2.Type) bool { |
| for { // loop for tail recursion |
| switch typ := typ0.(type) { |
| default: |
| base.Fatalf("unexpected type: %T", typ) |
| |
| case *types2.Basic, *types2.Named, *types2.TypeParam: |
| return false // named types cannot be part of an anonymous cycle |
| case *types2.Pointer: |
| typ0 = typ.Elem() |
| case *types2.Array: |
| typ0 = typ.Elem() |
| case *types2.Chan: |
| typ0 = typ.Elem() |
| case *types2.Map: |
| if f.visit(typ.Key()) { |
| return true |
| } |
| typ0 = typ.Elem() |
| case *types2.Slice: |
| typ0 = typ.Elem() |
| |
| case *types2.Struct: |
| for i := 0; i < typ.NumFields(); i++ { |
| if f.visit(typ.Field(i).Type()) { |
| return true |
| } |
| } |
| return false |
| |
| case *types2.Interface: |
| // The empty interface (e.g., "any") cannot be part of a cycle. |
| if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 0 { |
| return false |
| } |
| |
| // As an optimization, we wait to allocate cyclic here, after |
| // we've found at least one other (non-empty) anonymous |
| // interface. This means when a cycle is present, we need to |
| // make an extra recursive call to actually detect it. But for |
| // most packages, it allows skipping the map allocation |
| // entirely. |
| if x, ok := f.cyclic[typ]; ok { |
| return x |
| } |
| if f.cyclic == nil { |
| f.cyclic = make(map[*types2.Interface]bool) |
| } |
| f.cyclic[typ] = true |
| if f.hasCycle(typ) { |
| return true |
| } |
| f.cyclic[typ] = false |
| return false |
| |
| case *types2.Signature: |
| return f.visit(typ.Params()) || f.visit(typ.Results()) |
| case *types2.Tuple: |
| for i := 0; i < typ.Len(); i++ { |
| if f.visit(typ.At(i).Type()) { |
| return true |
| } |
| } |
| return false |
| } |
| } |
| } |