diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/irgen.go')
-rw-r--r-- | src/cmd/compile/internal/noder/irgen.go | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go new file mode 100644 index 0000000..d034926 --- /dev/null +++ b/src/cmd/compile/internal/noder/irgen.go @@ -0,0 +1,516 @@ +// 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 + } + } +} |