// 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 ( "os" "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" ) // check2 type checks a Go package using types2, and then generates IR // using the results. func check2(noders []*noder) { 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 conf := types2.Config{ InferFromConstraints: true, IgnoreBranches: true, // parser already checked via syntax.CheckBranches mode CompilerErrorMessages: true, // use error strings matching existing compiler errors Error: func(err error) { terr := err.(types2.Error) if len(terr.Msg) > 0 && terr.Msg[0] == '\t' { // types2 reports error clarifications via separate // error messages which are indented with a tab. // Ignore them to satisfy tools and tests that expect // only one error in such cases. // TODO(gri) Need to adjust error reporting in types2. return } base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg) }, Importer: &gcimports{ packages: make(map[string]*types2.Package), }, Sizes: &gcSizes{}, } info := types2.Info{ Types: make(map[syntax.Expr]types2.TypeAndValue), 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), // expand as needed } pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info) files = nil if err != nil { base.FatalfAt(src.NoXPos, "conf.Check error: %v", err) } base.ExitIfErrors() if base.Flag.G < 2 { os.Exit(0) } 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) if base.Flag.G < 3 { os.Exit(0) } } 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 } 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 mostly in 3 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 } } } types.LocalPkg.Height = myheight // 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 _, declList := range declLists { g.target.Decls = append(g.target.Decls, g.decls(declList)...) } typecheck.DeclareUniverse() 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.Walk(p.file, func(n syntax.Node) bool { g.validate(n) return false }) } } func (g *irgen) unhandled(what string, p poser) { base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p) panic("unreachable") }