blob: 3e0d3285ab916ec0661ba9601149a3209138f02c [file] [log] [blame]
// 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"
"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{
GoVersion: base.Flag.Lang,
IgnoreLabels: 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)
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),
Inferred: make(map[syntax.Expr]types2.Inferred),
// expand as needed
}
pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info)
files = nil
base.ExitIfErrors()
if err != nil {
base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
}
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
// Fully-instantiated generic types whose methods should be instantiated
instTypeList []*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 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)...)
}
if base.Flag.W > 1 {
for _, n := range g.target.Decls {
s := fmt.Sprintf("\nafter noder2 %v", n)
ir.Dump(s, n)
}
}
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
})
}
// Create any needed stencils of generic functions
g.stencil()
// For now, remove all generic functions from g.target.Decl, since they
// have been used for stenciling, but don't compile. TODO: We will
// eventually export any exportable generic functions.
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]
}
func (g *irgen) unhandled(what string, p poser) {
base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
panic("unreachable")
}