blob: d0349260e85468066fef25b929d4282fe781ec22 [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"
"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
}
}
}