Source file src/cmd/compile/internal/noder/irgen.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package noder
     6  
     7  import (
     8  	"fmt"
     9  	"internal/buildcfg"
    10  	"internal/types/errors"
    11  	"regexp"
    12  	"sort"
    13  
    14  	"cmd/compile/internal/base"
    15  	"cmd/compile/internal/midway"
    16  	"cmd/compile/internal/rangefunc"
    17  	"cmd/compile/internal/syntax"
    18  	"cmd/compile/internal/typecheck"
    19  	"cmd/compile/internal/types"
    20  	"cmd/compile/internal/types2"
    21  	"cmd/internal/src"
    22  )
    23  
    24  var versionErrorRx = regexp.MustCompile(`requires go[0-9]+\.[0-9]+ or later`)
    25  
    26  // checkFiles configures and runs the types2 checker on the given
    27  // parsed source files and then returns the result.
    28  // The map result value indicates which closures are generated from the bodies of range function loops.
    29  func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info, map[*syntax.FuncLit]bool) {
    30  	if base.SyntaxErrors() != 0 {
    31  		base.ErrorExit()
    32  	}
    33  
    34  	// setup and syntax error reporting
    35  	files := make([]*syntax.File, len(noders))
    36  	// fileBaseMap maps all file pos bases back to *syntax.File
    37  	// for checking Go version mismatched.
    38  	fileBaseMap := make(map[*syntax.PosBase]*syntax.File)
    39  	for i, p := range noders {
    40  		files[i] = p.file
    41  		// The file.Pos() is the position of the package clause.
    42  		// If there's a //line directive before that, file.Pos().Base()
    43  		// refers to that directive, not the file itself.
    44  		// Make sure to consistently map back to file base, here and
    45  		// when we look for a file in the conf.Error handler below,
    46  		// otherwise the file may not be found (was go.dev/issue/67141).
    47  		fileBaseMap[p.file.Pos().FileBase()] = p.file
    48  	}
    49  
    50  	didMidway := false
    51  
    52  recheck:
    53  	// typechecking
    54  	ctxt := types2.NewContext()
    55  	importer := gcimports{
    56  		ctxt:     ctxt,
    57  		packages: make(map[string]*types2.Package),
    58  	}
    59  	conf := types2.Config{
    60  		Context:            ctxt,
    61  		GoVersion:          base.Flag.Lang,
    62  		IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode
    63  		Importer:           &importer,
    64  		Sizes:              types2.SizesFor("gc", buildcfg.GOARCH),
    65  	}
    66  	if base.Flag.ErrorURL {
    67  		conf.ErrorURL = " [go.dev/e/%s]"
    68  	}
    69  	info := &types2.Info{
    70  		StoreTypesInSyntax: true,
    71  		Defs:               make(map[*syntax.Name]types2.Object),
    72  		Uses:               make(map[*syntax.Name]types2.Object),
    73  		Selections:         make(map[*syntax.SelectorExpr]*types2.Selection),
    74  		Implicits:          make(map[syntax.Node]types2.Object),
    75  		Scopes:             make(map[syntax.Node]*types2.Scope),
    76  		Instances:          make(map[*syntax.Name]types2.Instance),
    77  		FileVersions:       make(map[*syntax.PosBase]string),
    78  		// expand as needed
    79  	}
    80  	conf.Error = func(err error) {
    81  		terr := err.(types2.Error)
    82  		msg := terr.Msg
    83  		if versionErrorRx.MatchString(msg) {
    84  			fileBase := terr.Pos.FileBase()
    85  			fileVersion := info.FileVersions[fileBase]
    86  			file := fileBaseMap[fileBase]
    87  			if file == nil {
    88  				// This should never happen, but be careful and don't crash.
    89  			} else if file.GoVersion == fileVersion {
    90  				// If we have a version error caused by //go:build, report it.
    91  				msg = fmt.Sprintf("%s (file declares //go:build %s)", msg, fileVersion)
    92  			} else {
    93  				// Otherwise, hint at the -lang setting.
    94  				msg = fmt.Sprintf("%s (-lang was set to %s; check go.mod)", msg, base.Flag.Lang)
    95  			}
    96  		}
    97  		base.ErrorfAt(m.makeXPos(terr.Pos), terr.Code, "%s", msg)
    98  	}
    99  
   100  	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
   101  	base.ExitIfErrors()
   102  	if err != nil {
   103  		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
   104  	}
   105  
   106  	// Check for anonymous interface cycles (#56103).
   107  	// TODO(gri) move this code into the type checkers (types2 and go/types)
   108  	var f cycleFinder
   109  	for _, file := range files {
   110  		syntax.Inspect(file, func(n syntax.Node) bool {
   111  			if n, ok := n.(*syntax.InterfaceType); ok {
   112  				if f.hasCycle(types2.Unalias(n.GetTypeInfo().Type).(*types2.Interface)) {
   113  					base.ErrorfAt(m.makeXPos(n.Pos()), errors.InvalidTypeCycle, "invalid recursive type: anonymous interface refers to itself (see https://go.dev/issue/56103)")
   114  
   115  					for typ := range f.cyclic {
   116  						f.cyclic[typ] = false // suppress duplicate errors
   117  					}
   118  				}
   119  				return false
   120  			}
   121  			return true
   122  		})
   123  	}
   124  	base.ExitIfErrors()
   125  
   126  	// Implementation restriction: we don't allow not-in-heap types to
   127  	// be used as type arguments (#54765).
   128  	{
   129  		type nihTarg struct {
   130  			pos src.XPos
   131  			typ types2.Type
   132  		}
   133  		var nihTargs []nihTarg
   134  
   135  		for name, inst := range info.Instances {
   136  			for i := 0; i < inst.TypeArgs.Len(); i++ {
   137  				if targ := inst.TypeArgs.At(i); isNotInHeap(targ) {
   138  					nihTargs = append(nihTargs, nihTarg{m.makeXPos(name.Pos()), targ})
   139  				}
   140  			}
   141  		}
   142  		sort.Slice(nihTargs, func(i, j int) bool {
   143  			ti, tj := nihTargs[i], nihTargs[j]
   144  			return ti.pos.Before(tj.pos)
   145  		})
   146  		for _, targ := range nihTargs {
   147  			base.ErrorfAt(targ.pos, 0, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ)
   148  		}
   149  	}
   150  	base.ExitIfErrors()
   151  
   152  	// Implementation restriction: we don't allow not-in-heap types to
   153  	// be used as map keys/values, or channel.
   154  	{
   155  		for _, file := range files {
   156  			syntax.Inspect(file, func(n syntax.Node) bool {
   157  				if n, ok := n.(*syntax.TypeDecl); ok {
   158  					switch n := n.Type.(type) {
   159  					case *syntax.MapType:
   160  						typ := n.GetTypeInfo().Type.Underlying().(*types2.Map)
   161  						if isNotInHeap(typ.Key()) {
   162  							base.ErrorfAt(m.makeXPos(n.Pos()), 0, "incomplete (or unallocatable) map key not allowed")
   163  						}
   164  						if isNotInHeap(typ.Elem()) {
   165  							base.ErrorfAt(m.makeXPos(n.Pos()), 0, "incomplete (or unallocatable) map value not allowed")
   166  						}
   167  					case *syntax.ChanType:
   168  						typ := n.GetTypeInfo().Type.Underlying().(*types2.Chan)
   169  						if isNotInHeap(typ.Elem()) {
   170  							base.ErrorfAt(m.makeXPos(n.Pos()), 0, "chan of incomplete (or unallocatable) type not allowed")
   171  						}
   172  					}
   173  				}
   174  				return true
   175  			})
   176  		}
   177  	}
   178  	base.ExitIfErrors()
   179  
   180  	if len(base.Debug.AstDump) > 0 {
   181  		dumpSyntax(pkg, info, files, "checked")
   182  	}
   183  
   184  	if buildcfg.Experiment.SIMD && !didMidway {
   185  		didMidway = true
   186  		// Perform midway transformation on AST directly
   187  		if midway.RewriteWrapper(pkg, info, files) {
   188  			// midway made changes; type checking must be repeated.
   189  			if len(base.Debug.AstDump) > 0 {
   190  				// TODO how should this interact with -W and textual dumps
   191  				dumpSyntax(pkg, info, files, "midway before recheck")
   192  			}
   193  			// necessary to reset type checking
   194  			for _, p := range types.PkgMap() {
   195  				p.Direct = false
   196  			}
   197  			// necessary to reset type checking
   198  			typecheck.Target.Imports = nil
   199  			goto recheck
   200  		}
   201  	}
   202  
   203  	if len(base.Debug.AstDump) > 0 {
   204  		dumpSyntax(pkg, info, files, "midway after recheck")
   205  	}
   206  
   207  	// Rewrite range over function to explicit function calls
   208  	// with the loop bodies converted into new implicit closures.
   209  	// We do this now, before serialization to unified IR, so that if the
   210  	// implicit closures are inlined, we will have the unified IR form.
   211  	// If we do the rewrite in the back end, like between typecheck and walk,
   212  	// then the new implicit closure will not have a unified IR inline body,
   213  	// and bodyReaderFor will fail.
   214  	rangeInfo := rangefunc.Rewrite(pkg, info, files)
   215  
   216  	if len(base.Debug.AstDump) > 0 {
   217  		dumpSyntax(pkg, info, files, "rangefunc")
   218  	}
   219  
   220  	return pkg, info, rangeInfo
   221  }
   222  
   223  func dumpSyntax(pkg *types2.Package, info *types2.Info, files []*syntax.File, phase string) {
   224  	for _, file := range files {
   225  		for _, decl := range file.DeclList {
   226  			if fn, ok := decl.(*syntax.FuncDecl); ok {
   227  				if MatchASTDump(fn) {
   228  					DumpNodeHTML(pkg, file, info, fn, phase, fn)
   229  				}
   230  			}
   231  		}
   232  	}
   233  }
   234  
   235  // A cycleFinder detects anonymous interface cycles (go.dev/issue/56103).
   236  type cycleFinder struct {
   237  	cyclic map[*types2.Interface]bool
   238  }
   239  
   240  // hasCycle reports whether typ is part of an anonymous interface cycle.
   241  func (f *cycleFinder) hasCycle(typ *types2.Interface) bool {
   242  	// We use Method instead of ExplicitMethod to implicitly expand any
   243  	// embedded interfaces. Then we just need to walk any anonymous
   244  	// types, keeping track of *types2.Interface types we visit along
   245  	// the way.
   246  	for i := 0; i < typ.NumMethods(); i++ {
   247  		if f.visit(typ.Method(i).Type()) {
   248  			return true
   249  		}
   250  	}
   251  	return false
   252  }
   253  
   254  // visit recursively walks typ0 to check any referenced interface types.
   255  func (f *cycleFinder) visit(typ0 types2.Type) bool {
   256  	for { // loop for tail recursion
   257  		switch typ := types2.Unalias(typ0).(type) {
   258  		default:
   259  			base.Fatalf("unexpected type: %T", typ)
   260  
   261  		case *types2.Basic, *types2.Named, *types2.TypeParam:
   262  			return false // named types cannot be part of an anonymous cycle
   263  		case *types2.Pointer:
   264  			typ0 = typ.Elem()
   265  		case *types2.Array:
   266  			typ0 = typ.Elem()
   267  		case *types2.Chan:
   268  			typ0 = typ.Elem()
   269  		case *types2.Map:
   270  			if f.visit(typ.Key()) {
   271  				return true
   272  			}
   273  			typ0 = typ.Elem()
   274  		case *types2.Slice:
   275  			typ0 = typ.Elem()
   276  
   277  		case *types2.Struct:
   278  			for i := 0; i < typ.NumFields(); i++ {
   279  				if f.visit(typ.Field(i).Type()) {
   280  					return true
   281  				}
   282  			}
   283  			return false
   284  
   285  		case *types2.Interface:
   286  			// The empty interface (e.g., "any") cannot be part of a cycle.
   287  			if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 0 {
   288  				return false
   289  			}
   290  
   291  			// As an optimization, we wait to allocate cyclic here, after
   292  			// we've found at least one other (non-empty) anonymous
   293  			// interface. This means when a cycle is present, we need to
   294  			// make an extra recursive call to actually detect it. But for
   295  			// most packages, it allows skipping the map allocation
   296  			// entirely.
   297  			if x, ok := f.cyclic[typ]; ok {
   298  				return x
   299  			}
   300  			if f.cyclic == nil {
   301  				f.cyclic = make(map[*types2.Interface]bool)
   302  			}
   303  			f.cyclic[typ] = true
   304  			if f.hasCycle(typ) {
   305  				return true
   306  			}
   307  			f.cyclic[typ] = false
   308  			return false
   309  
   310  		case *types2.Signature:
   311  			return f.visit(typ.Params()) || f.visit(typ.Results())
   312  		case *types2.Tuple:
   313  			for i := 0; i < typ.Len(); i++ {
   314  				if f.visit(typ.At(i).Type()) {
   315  					return true
   316  				}
   317  			}
   318  			return false
   319  		}
   320  	}
   321  }
   322  

View as plain text