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

View as plain text